@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.
package/dist/index.mjs CHANGED
@@ -26877,6 +26877,30 @@ function MoreIcon4() {
26877
26877
  }
26878
26878
  );
26879
26879
  }
26880
+ function DisclosureIcon({ expanded }) {
26881
+ return /* @__PURE__ */ jsx(
26882
+ "svg",
26883
+ {
26884
+ "aria-hidden": "true",
26885
+ viewBox: "0 0 20 20",
26886
+ className: cn(
26887
+ "h-4 w-4 transition-transform motion-reduce:transition-none",
26888
+ expanded && "rotate-180"
26889
+ ),
26890
+ fill: "none",
26891
+ children: /* @__PURE__ */ jsx(
26892
+ "path",
26893
+ {
26894
+ d: "m6 8 4 4 4-4",
26895
+ stroke: "currentColor",
26896
+ strokeWidth: "1.6",
26897
+ strokeLinecap: "round",
26898
+ strokeLinejoin: "round"
26899
+ }
26900
+ )
26901
+ }
26902
+ );
26903
+ }
26880
26904
  function assignRef3(ref, node) {
26881
26905
  if (typeof ref === "function") {
26882
26906
  ref(node);
@@ -27032,6 +27056,94 @@ function resolveNavigationTreeClassNames(classNames) {
27032
27056
  )
27033
27057
  };
27034
27058
  }
27059
+ function entryClassName({
27060
+ item,
27061
+ active,
27062
+ collapsed,
27063
+ mobile,
27064
+ embedded = false,
27065
+ appearance,
27066
+ classNames,
27067
+ domClassName
27068
+ }) {
27069
+ return cn(
27070
+ "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",
27071
+ appearance.itemHeight,
27072
+ appearance.itemPadding,
27073
+ 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",
27074
+ collapsed && !mobile && "justify-center px-0",
27075
+ item.disabled && "pointer-events-none opacity-50",
27076
+ classNames?.item,
27077
+ active && classNames?.itemActive,
27078
+ mobile && classNames?.mobileItem,
27079
+ domClassName,
27080
+ item.className
27081
+ );
27082
+ }
27083
+ function EntryContent({
27084
+ item,
27085
+ collapsed,
27086
+ mobile,
27087
+ classNames,
27088
+ endIndicator
27089
+ }) {
27090
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
27091
+ hasRenderableNode(item.icon) ? /* @__PURE__ */ jsx(
27092
+ "span",
27093
+ {
27094
+ className: cn(
27095
+ "flex size-5 shrink-0 items-center justify-center [&_svg]:size-full",
27096
+ classNames?.itemIcon
27097
+ ),
27098
+ "aria-hidden": "true",
27099
+ children: item.icon
27100
+ }
27101
+ ) : null,
27102
+ mobile || !collapsed ? /* @__PURE__ */ jsxs(
27103
+ "span",
27104
+ {
27105
+ className: cn(
27106
+ "flex min-w-0 flex-1 flex-col",
27107
+ classNames?.itemContent
27108
+ ),
27109
+ children: [
27110
+ /* @__PURE__ */ jsx(
27111
+ "span",
27112
+ {
27113
+ className: cn(
27114
+ "min-w-0 truncate font-semibold",
27115
+ classNames?.itemLabel
27116
+ ),
27117
+ children: item.label
27118
+ }
27119
+ ),
27120
+ hasRenderableNode(item.description) ? /* @__PURE__ */ jsx(
27121
+ "span",
27122
+ {
27123
+ className: cn(
27124
+ "mt-0.5 truncate text-xs font-normal",
27125
+ classNames?.itemDescription
27126
+ ),
27127
+ children: item.description
27128
+ }
27129
+ ) : null
27130
+ ]
27131
+ }
27132
+ ) : null,
27133
+ (mobile || !collapsed) && hasRenderableNode(item.meta) ? /* @__PURE__ */ jsx(
27134
+ "span",
27135
+ {
27136
+ className: cn(
27137
+ "shrink-0 text-xs text-muted-foreground",
27138
+ classNames?.itemMeta
27139
+ ),
27140
+ children: item.meta
27141
+ }
27142
+ ) : null,
27143
+ (mobile || !collapsed) && hasRenderableNode(item.endContent) ? /* @__PURE__ */ jsx("span", { className: cn("shrink-0", classNames?.itemEndContent), children: item.endContent }) : null,
27144
+ (mobile || !collapsed) && hasRenderableNode(endIndicator) ? /* @__PURE__ */ jsx("span", { className: "flex shrink-0 items-center justify-center", children: endIndicator }) : null
27145
+ ] });
27146
+ }
27035
27147
  function EntryButton({
27036
27148
  item,
27037
27149
  active,
@@ -27040,7 +27152,11 @@ function EntryButton({
27040
27152
  appearance,
27041
27153
  classNames,
27042
27154
  mobile = false,
27043
- onSelect
27155
+ embedded = false,
27156
+ onSelect,
27157
+ ariaControls,
27158
+ ariaExpanded,
27159
+ endIndicator
27044
27160
  }) {
27045
27161
  const ItemComponent = itemAs ?? (item.href ? "a" : "button");
27046
27162
  const title = getNodeTitle(item.label, item.title);
@@ -27057,90 +27173,90 @@ function EntryButton({
27057
27173
  ...domProps,
27058
27174
  ...item.to ? { to: item.to } : {},
27059
27175
  ...item.href ? { href: item.href } : {},
27060
- ...ItemComponent === "button" ? { type: "button" } : {},
27176
+ ...ItemComponent === "button" ? { type: "button", disabled: item.disabled } : {},
27061
27177
  title,
27062
27178
  "aria-current": active ? "page" : void 0,
27063
27179
  "aria-disabled": item.disabled || void 0,
27180
+ "aria-controls": ariaControls,
27181
+ "aria-expanded": ariaExpanded,
27064
27182
  tabIndex: item.disabled ? -1 : domProps.tabIndex,
27065
27183
  onClick: handleClick
27066
27184
  };
27067
- return /* @__PURE__ */ jsxs(
27185
+ return /* @__PURE__ */ jsx(
27068
27186
  ItemComponent,
27069
27187
  {
27070
27188
  ...componentProps,
27071
27189
  "data-navigation-sidebar-item-id": item.id,
27072
27190
  "data-active": active ? "true" : "false",
27073
- className: cn(
27074
- "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",
27075
- appearance.itemHeight,
27076
- appearance.itemPadding,
27077
- active ? activeVariantClassName(appearance.activeVariant) : "text-muted-foreground hover:border-border/70 hover:bg-muted/60 hover:text-foreground",
27078
- collapsed && !mobile && "justify-center px-0",
27079
- item.disabled && "pointer-events-none opacity-50",
27080
- classNames?.item,
27081
- active && classNames?.itemActive,
27082
- mobile && classNames?.mobileItem,
27083
- domProps.className,
27084
- item.className
27085
- ),
27086
- children: [
27087
- hasRenderableNode(item.icon) ? /* @__PURE__ */ jsx(
27088
- "span",
27089
- {
27090
- className: cn(
27091
- "flex size-5 shrink-0 items-center justify-center [&_svg]:size-full",
27092
- classNames?.itemIcon
27093
- ),
27094
- "aria-hidden": "true",
27095
- children: item.icon
27096
- }
27097
- ) : null,
27098
- mobile || !collapsed ? /* @__PURE__ */ jsxs(
27099
- "span",
27100
- {
27101
- className: cn(
27102
- "flex min-w-0 flex-1 flex-col",
27103
- classNames?.itemContent
27104
- ),
27105
- children: [
27106
- /* @__PURE__ */ jsx(
27107
- "span",
27108
- {
27109
- className: cn(
27110
- "min-w-0 truncate font-semibold",
27111
- classNames?.itemLabel
27112
- ),
27113
- children: item.label
27114
- }
27115
- ),
27116
- hasRenderableNode(item.description) ? /* @__PURE__ */ jsx(
27117
- "span",
27118
- {
27119
- className: cn(
27120
- "mt-0.5 truncate text-xs font-normal",
27121
- classNames?.itemDescription
27122
- ),
27123
- children: item.description
27124
- }
27125
- ) : null
27126
- ]
27127
- }
27128
- ) : null,
27129
- (mobile || !collapsed) && hasRenderableNode(item.meta) ? /* @__PURE__ */ jsx(
27130
- "span",
27131
- {
27132
- className: cn(
27133
- "shrink-0 text-xs text-muted-foreground",
27134
- classNames?.itemMeta
27135
- ),
27136
- children: item.meta
27137
- }
27138
- ) : null,
27139
- (mobile || !collapsed) && hasRenderableNode(item.endContent) ? /* @__PURE__ */ jsx("span", { className: cn("shrink-0", classNames?.itemEndContent), children: item.endContent }) : null
27140
- ]
27191
+ "data-expanded": ariaExpanded === void 0 ? void 0 : String(ariaExpanded),
27192
+ className: entryClassName({
27193
+ item,
27194
+ active,
27195
+ collapsed,
27196
+ mobile,
27197
+ embedded,
27198
+ appearance,
27199
+ classNames,
27200
+ domClassName: domProps.className
27201
+ }),
27202
+ children: /* @__PURE__ */ jsx(
27203
+ EntryContent,
27204
+ {
27205
+ item,
27206
+ collapsed,
27207
+ mobile,
27208
+ classNames,
27209
+ endIndicator
27210
+ }
27211
+ )
27141
27212
  }
27142
27213
  );
27143
27214
  }
27215
+ function EntryPresentation({
27216
+ item,
27217
+ appearance,
27218
+ classNames
27219
+ }) {
27220
+ const domProps = item.domProps ?? {};
27221
+ return /* @__PURE__ */ jsx(
27222
+ "div",
27223
+ {
27224
+ ...domProps,
27225
+ "data-navigation-sidebar-item-id": item.id,
27226
+ "aria-disabled": item.disabled || void 0,
27227
+ title: getNodeTitle(item.label, item.title),
27228
+ className: entryClassName({
27229
+ item,
27230
+ active: false,
27231
+ collapsed: false,
27232
+ mobile: true,
27233
+ embedded: true,
27234
+ appearance,
27235
+ classNames,
27236
+ domClassName: domProps.className
27237
+ }),
27238
+ children: /* @__PURE__ */ jsx(
27239
+ EntryContent,
27240
+ {
27241
+ item,
27242
+ collapsed: false,
27243
+ mobile: true,
27244
+ classNames
27245
+ }
27246
+ )
27247
+ }
27248
+ );
27249
+ }
27250
+ function routeRowClassName({
27251
+ active,
27252
+ appearance
27253
+ }) {
27254
+ return cn(
27255
+ "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",
27256
+ appearance.itemHeight,
27257
+ active ? activeVariantClassName(appearance.activeVariant) : "text-muted-foreground hover:border-border/70 hover:bg-muted/60 hover:text-foreground"
27258
+ );
27259
+ }
27144
27260
  function filterCatalogItems(items, activeFilterId) {
27145
27261
  if (!activeFilterId) return items;
27146
27262
  return items.filter(
@@ -27272,7 +27388,9 @@ function ChildEntry({
27272
27388
  active,
27273
27389
  interactive,
27274
27390
  onSelect,
27275
- classNames
27391
+ appearance,
27392
+ classNames,
27393
+ embedded = false
27276
27394
  }) {
27277
27395
  const Component = interactive ? "button" : "div";
27278
27396
  const domProps = child.domProps ?? {};
@@ -27287,8 +27405,11 @@ function ChildEntry({
27287
27405
  title: getNodeTitle(child.label, child.title),
27288
27406
  disabled: interactive ? child.disabled : void 0,
27289
27407
  className: cn(
27290
- "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",
27291
- active ? "border-primary/35 bg-primary/10 text-primary" : "text-muted-foreground hover:border-border hover:bg-muted/60 hover:text-foreground",
27408
+ "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",
27409
+ appearance.itemHeight,
27410
+ appearance.itemPadding,
27411
+ "pl-11",
27412
+ 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",
27292
27413
  child.disabled && "pointer-events-none opacity-50",
27293
27414
  classNames?.childItem,
27294
27415
  active && classNames?.childItemActive,
@@ -27300,7 +27421,7 @@ function ChildEntry({
27300
27421
  hasRenderableNode(child.icon) ? /* @__PURE__ */ jsx(
27301
27422
  "span",
27302
27423
  {
27303
- className: "flex size-4 shrink-0 items-center justify-center",
27424
+ className: "flex size-5 shrink-0 items-center justify-center [&_svg]:size-full",
27304
27425
  "aria-hidden": "true",
27305
27426
  children: child.icon
27306
27427
  }
@@ -27310,29 +27431,167 @@ function ChildEntry({
27310
27431
  }
27311
27432
  );
27312
27433
  }
27434
+ function RouteActions({
27435
+ itemId,
27436
+ actions,
27437
+ kind,
27438
+ className,
27439
+ mobile = false
27440
+ }) {
27441
+ if (!hasRenderableNode(actions)) return null;
27442
+ return /* @__PURE__ */ jsx(
27443
+ "div",
27444
+ {
27445
+ "data-navigation-sidebar-item-actions": kind === "item" ? itemId : void 0,
27446
+ "data-navigation-sidebar-child-actions": kind === "child" ? itemId : void 0,
27447
+ "data-navigation-sidebar-actions-mobile": mobile ? "true" : void 0,
27448
+ className: cn(
27449
+ "ml-auto flex shrink-0 items-center gap-1 [&>button]:border-0 [&>button]:bg-transparent [&>button]:shadow-none [&>button:hover]:bg-muted",
27450
+ className
27451
+ ),
27452
+ children: actions
27453
+ }
27454
+ );
27455
+ }
27456
+ function isDisclosureRouteItem(item) {
27457
+ return item.interaction === "disclosure";
27458
+ }
27459
+ function validateRouteItems(routeItems) {
27460
+ const itemIds = /* @__PURE__ */ new Set();
27461
+ const childIds = /* @__PURE__ */ new Set();
27462
+ const collapsibleItemIds = /* @__PURE__ */ new Set();
27463
+ for (const item of routeItems) {
27464
+ const itemId = item.id;
27465
+ if (itemIds.has(itemId)) {
27466
+ throw new RangeError(
27467
+ `NavigationSidebar route item ids must be unique; received "${itemId}" more than once.`
27468
+ );
27469
+ }
27470
+ itemIds.add(itemId);
27471
+ const disclosure = isDisclosureRouteItem(item);
27472
+ if (disclosure) {
27473
+ if (!item.children.length) {
27474
+ throw new RangeError(
27475
+ `NavigationSidebar disclosure item "${item.id}" must include at least one child.`
27476
+ );
27477
+ }
27478
+ if (item.href !== void 0 || item.to !== void 0 || item.onClick !== void 0) {
27479
+ throw new TypeError(
27480
+ `NavigationSidebar disclosure item "${itemId}" cannot define href, to, or onClick.`
27481
+ );
27482
+ }
27483
+ collapsibleItemIds.add(item.id);
27484
+ } else if (item.childrenPresentation === "collapsible") {
27485
+ if (!item.children?.length) {
27486
+ throw new RangeError(
27487
+ `NavigationSidebar collapsible route item "${item.id}" must include at least one child.`
27488
+ );
27489
+ }
27490
+ collapsibleItemIds.add(item.id);
27491
+ }
27492
+ for (const child of item.children ?? []) {
27493
+ if (childIds.has(child.id)) {
27494
+ throw new RangeError(
27495
+ `NavigationSidebar route child ids must be unique; received "${child.id}" more than once.`
27496
+ );
27497
+ }
27498
+ childIds.add(child.id);
27499
+ }
27500
+ }
27501
+ return collapsibleItemIds;
27502
+ }
27503
+ function RouteChildrenToggle({
27504
+ item,
27505
+ expanded,
27506
+ controls,
27507
+ label,
27508
+ onToggle,
27509
+ classNames
27510
+ }) {
27511
+ const itemLabel = typeof item.label === "string" ? item.label : item.id;
27512
+ return /* @__PURE__ */ jsx(
27513
+ "button",
27514
+ {
27515
+ type: "button",
27516
+ disabled: item.disabled,
27517
+ "aria-expanded": expanded,
27518
+ "aria-controls": controls,
27519
+ "aria-label": `${label}: ${itemLabel}`,
27520
+ title: label,
27521
+ "data-navigation-sidebar-disclosure-trigger": item.id,
27522
+ className: cn(
27523
+ "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",
27524
+ classNames?.childMenuTrigger
27525
+ ),
27526
+ onClick: onToggle,
27527
+ children: /* @__PURE__ */ jsx(DisclosureIcon, { expanded })
27528
+ }
27529
+ );
27530
+ }
27313
27531
  function CollapsedChildMenu({
27314
27532
  item,
27315
27533
  activeChildId,
27316
27534
  label,
27317
27535
  onChildSelect,
27318
- classNames
27536
+ classNames,
27537
+ appearance,
27538
+ triggerVariant = "submenu",
27539
+ mobile = false
27319
27540
  }) {
27320
27541
  const enabledChildren = (item.children ?? []).filter(
27321
27542
  (child) => !child.disabled
27322
27543
  );
27323
- if (enabledChildren.length === 0) return null;
27544
+ if (!item.children?.length) return null;
27545
+ if (triggerVariant === "submenu" && (!onChildSelect || enabledChildren.length === 0))
27546
+ return null;
27547
+ const itemLabel = typeof item.label === "string" ? item.label : item.id;
27548
+ const itemTriggerDisabled = Boolean(item.disabled) || !onChildSelect || enabledChildren.length === 0;
27549
+ const domProps = item.domProps ?? {};
27324
27550
  return /* @__PURE__ */ jsxs(DropdownMenu2.Root, { children: [
27325
- /* @__PURE__ */ jsx(DropdownMenu2.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(
27551
+ /* @__PURE__ */ jsx(DropdownMenu2.Trigger, { asChild: true, children: triggerVariant === "item" && appearance ? /* @__PURE__ */ jsx(
27552
+ "button",
27553
+ {
27554
+ ...domProps,
27555
+ type: "button",
27556
+ disabled: itemTriggerDisabled,
27557
+ "aria-label": `${label}: ${itemLabel}`,
27558
+ title: label,
27559
+ "data-navigation-sidebar-item-id": item.id,
27560
+ "data-navigation-sidebar-disclosure-menu": "true",
27561
+ className: entryClassName({
27562
+ item,
27563
+ active: false,
27564
+ collapsed: !mobile,
27565
+ mobile,
27566
+ appearance,
27567
+ classNames,
27568
+ domClassName: domProps.className
27569
+ }),
27570
+ children: /* @__PURE__ */ jsx(
27571
+ EntryContent,
27572
+ {
27573
+ item,
27574
+ collapsed: !mobile,
27575
+ mobile,
27576
+ classNames,
27577
+ endIndicator: mobile ? /* @__PURE__ */ jsx(DisclosureIcon, { expanded: false }) : void 0
27578
+ }
27579
+ )
27580
+ }
27581
+ ) : /* @__PURE__ */ jsx(
27326
27582
  "button",
27327
27583
  {
27328
27584
  type: "button",
27585
+ disabled: triggerVariant === "disclosure" ? itemTriggerDisabled : void 0,
27586
+ "data-navigation-sidebar-disclosure-trigger": triggerVariant === "disclosure" ? item.id : void 0,
27329
27587
  className: cn(
27330
- "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",
27588
+ "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",
27589
+ triggerVariant === "disclosure" ? "w-7" : "w-6",
27331
27590
  classNames?.childMenuTrigger
27332
27591
  ),
27333
- "aria-label": `${label}: ${typeof item.label === "string" ? item.label : item.id}`,
27592
+ "aria-label": `${label}: ${itemLabel}`,
27334
27593
  title: label,
27335
- children: /* @__PURE__ */ jsx(MoreIcon4, {})
27594
+ children: triggerVariant === "disclosure" ? /* @__PURE__ */ jsx(DisclosureIcon, { expanded: false }) : /* @__PURE__ */ jsx(MoreIcon4, {})
27336
27595
  }
27337
27596
  ) }),
27338
27597
  /* @__PURE__ */ jsx(DropdownMenu2.Portal, { children: /* @__PURE__ */ jsx(
@@ -27355,7 +27614,7 @@ function CollapsedChildMenu({
27355
27614
  "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",
27356
27615
  child.id === activeChildId && "bg-accent text-accent-foreground"
27357
27616
  ),
27358
- onSelect: () => onChildSelect(child, item),
27617
+ onSelect: () => onChildSelect?.(child, item),
27359
27618
  children: [
27360
27619
  hasRenderableNode(child.icon) ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: child.icon }) : null,
27361
27620
  /* @__PURE__ */ jsx("span", { className: "truncate", children: child.label })
@@ -27376,22 +27635,81 @@ function RouteContent({
27376
27635
  routeItems,
27377
27636
  activeItemId,
27378
27637
  activeChildId,
27638
+ expandedItemIds,
27639
+ defaultExpandedItemIds = [],
27379
27640
  itemAs,
27380
27641
  onItemSelect,
27381
27642
  onChildSelect,
27643
+ onExpandedItemIdsChange,
27382
27644
  collapsedChildMenuLabel = "Open submenu",
27645
+ disclosureLabel = "Toggle submenu",
27383
27646
  ariaLabel,
27384
27647
  classNames
27385
27648
  } = props;
27386
- 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) => {
27387
- const active = item.id === activeItemId;
27649
+ const disclosureId = useId();
27650
+ const collapsibleItemIds = useMemo(
27651
+ () => validateRouteItems(routeItems),
27652
+ [routeItems]
27653
+ );
27654
+ const [internalExpandedItemIds, setInternalExpandedItemIds] = useState(
27655
+ () => defaultExpandedItemIds
27656
+ );
27657
+ const isExpandedControlled = expandedItemIds !== void 0;
27658
+ const resolvedExpandedItemIds = expandedItemIds ?? internalExpandedItemIds;
27659
+ const normalizedExpandedItemIds = useMemo(
27660
+ () => resolvedExpandedItemIds.filter(
27661
+ (itemId) => collapsibleItemIds.has(itemId)
27662
+ ),
27663
+ [collapsibleItemIds, resolvedExpandedItemIds]
27664
+ );
27665
+ const expandedItemIdSet = useMemo(
27666
+ () => new Set(normalizedExpandedItemIds),
27667
+ [normalizedExpandedItemIds]
27668
+ );
27669
+ const toggleItemExpanded = useCallback(
27670
+ (item) => {
27671
+ if (item.disabled || !collapsibleItemIds.has(item.id)) return;
27672
+ const nextExpandedItemIds = expandedItemIdSet.has(item.id) ? normalizedExpandedItemIds.filter((itemId) => itemId !== item.id) : [...normalizedExpandedItemIds, item.id];
27673
+ if (!isExpandedControlled) {
27674
+ setInternalExpandedItemIds(nextExpandedItemIds);
27675
+ }
27676
+ onExpandedItemIdsChange?.(nextExpandedItemIds);
27677
+ },
27678
+ [
27679
+ collapsibleItemIds,
27680
+ expandedItemIdSet,
27681
+ isExpandedControlled,
27682
+ normalizedExpandedItemIds,
27683
+ onExpandedItemIdsChange
27684
+ ]
27685
+ );
27686
+ 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) => {
27687
+ const disclosure = isDisclosureRouteItem(item);
27688
+ const active = !disclosure && item.id === activeItemId;
27689
+ const hasChildren = Boolean(item.children?.length);
27690
+ const childrenCollapsible = collapsibleItemIds.has(item.id);
27691
+ const childrenExpanded = !childrenCollapsible || expandedItemIdSet.has(item.id);
27692
+ const showItemActions = !collapsed && hasRenderableNode(item.actions);
27693
+ const compoundItemRow = collapsed && hasChildren && Boolean(onChildSelect) || !collapsed && childrenCollapsible && !disclosure || showItemActions;
27694
+ const childListId = `${disclosureId}-route-children-${itemIndex}`;
27388
27695
  return /* @__PURE__ */ jsxs("li", { className: "grid min-w-0 gap-1", children: [
27389
- /* @__PURE__ */ jsxs(
27696
+ collapsed && disclosure ? /* @__PURE__ */ jsx(
27697
+ CollapsedChildMenu,
27698
+ {
27699
+ item,
27700
+ activeChildId,
27701
+ label: collapsedChildMenuLabel,
27702
+ onChildSelect,
27703
+ classNames,
27704
+ appearance,
27705
+ triggerVariant: "item"
27706
+ }
27707
+ ) : /* @__PURE__ */ jsxs(
27390
27708
  "div",
27391
27709
  {
27392
- className: cn(
27393
- collapsed && Boolean(item.children?.length) && Boolean(onChildSelect) && "flex min-w-0 items-center gap-0.5"
27394
- ),
27710
+ "data-navigation-sidebar-item-row": compoundItemRow ? item.id : void 0,
27711
+ "data-active": compoundItemRow ? String(active) : void 0,
27712
+ className: compoundItemRow ? routeRowClassName({ active, appearance }) : void 0,
27395
27713
  children: [
27396
27714
  /* @__PURE__ */ jsx(
27397
27715
  EntryButton,
@@ -27399,12 +27717,25 @@ function RouteContent({
27399
27717
  item,
27400
27718
  active,
27401
27719
  collapsed,
27402
- itemAs,
27720
+ embedded: compoundItemRow,
27721
+ itemAs: disclosure ? "button" : itemAs,
27403
27722
  appearance,
27404
27723
  classNames,
27405
- onSelect: onItemSelect ? () => onItemSelect(item) : void 0
27724
+ onSelect: disclosure ? () => toggleItemExpanded(item) : onItemSelect ? () => onItemSelect(item) : void 0,
27725
+ ariaControls: !collapsed && childrenCollapsible && disclosure ? childListId : void 0,
27726
+ ariaExpanded: !collapsed && childrenCollapsible && disclosure ? childrenExpanded : void 0,
27727
+ endIndicator: !collapsed && disclosure && !showItemActions ? /* @__PURE__ */ jsx(DisclosureIcon, { expanded: childrenExpanded }) : void 0
27406
27728
  }
27407
27729
  ),
27730
+ showItemActions ? /* @__PURE__ */ jsx(
27731
+ RouteActions,
27732
+ {
27733
+ itemId: item.id,
27734
+ actions: item.actions,
27735
+ kind: "item",
27736
+ className: classNames?.itemActions
27737
+ }
27738
+ ) : null,
27408
27739
  collapsed && onChildSelect ? /* @__PURE__ */ jsx(
27409
27740
  CollapsedChildMenu,
27410
27741
  {
@@ -27414,28 +27745,71 @@ function RouteContent({
27414
27745
  onChildSelect,
27415
27746
  classNames
27416
27747
  }
27748
+ ) : null,
27749
+ !collapsed && childrenCollapsible && (!disclosure || showItemActions) ? /* @__PURE__ */ jsx(
27750
+ RouteChildrenToggle,
27751
+ {
27752
+ item,
27753
+ expanded: childrenExpanded,
27754
+ controls: childListId,
27755
+ label: disclosureLabel,
27756
+ onToggle: () => toggleItemExpanded(item),
27757
+ classNames
27758
+ }
27417
27759
  ) : null
27418
27760
  ]
27419
27761
  }
27420
27762
  ),
27421
- !collapsed && item.children?.length ? /* @__PURE__ */ jsx(
27763
+ !collapsed && hasChildren ? /* @__PURE__ */ jsx(
27422
27764
  "ul",
27423
27765
  {
27766
+ id: childListId,
27767
+ hidden: !childrenExpanded,
27768
+ style: childrenExpanded ? void 0 : { display: "none" },
27424
27769
  className: cn(
27425
27770
  "ml-5 grid list-none gap-0.5 border-l border-border/70 py-1 pl-3",
27426
27771
  classNames?.childList
27427
27772
  ),
27428
- children: item.children.map((child) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
27429
- ChildEntry,
27430
- {
27431
- child,
27432
- parent: item,
27433
- active: child.id === activeChildId,
27434
- interactive: Boolean(onChildSelect),
27435
- onSelect: onChildSelect,
27436
- classNames
27437
- }
27438
- ) }, child.id))
27773
+ children: item.children?.map((child) => {
27774
+ const hasChildActions = hasRenderableNode(child.actions);
27775
+ const childActive = child.id === activeChildId;
27776
+ const childEntry = /* @__PURE__ */ jsx(
27777
+ ChildEntry,
27778
+ {
27779
+ child,
27780
+ parent: item,
27781
+ active: childActive,
27782
+ interactive: Boolean(onChildSelect),
27783
+ onSelect: onChildSelect,
27784
+ appearance,
27785
+ classNames,
27786
+ embedded: hasChildActions
27787
+ }
27788
+ );
27789
+ return /* @__PURE__ */ jsx("li", { children: hasChildActions ? /* @__PURE__ */ jsxs(
27790
+ "div",
27791
+ {
27792
+ "data-navigation-sidebar-child-row": child.id,
27793
+ "data-active": String(childActive),
27794
+ className: routeRowClassName({
27795
+ active: childActive,
27796
+ appearance
27797
+ }),
27798
+ children: [
27799
+ childEntry,
27800
+ /* @__PURE__ */ jsx(
27801
+ RouteActions,
27802
+ {
27803
+ itemId: child.id,
27804
+ actions: child.actions,
27805
+ kind: "child",
27806
+ className: classNames?.childActions
27807
+ }
27808
+ )
27809
+ ]
27810
+ }
27811
+ ) : childEntry }, child.id);
27812
+ })
27439
27813
  }
27440
27814
  ) : null
27441
27815
  ] }, item.id);
@@ -27469,23 +27843,116 @@ function MobileNavigation({
27469
27843
  classNames?.mobileNav,
27470
27844
  props.mobile.className
27471
27845
  ),
27472
- children: items.map((item) => /* @__PURE__ */ jsx(
27473
- EntryButton,
27474
- {
27475
- item,
27476
- active: props.kind === "catalog" ? item.active ?? item.id === props.activeItemId : item.id === props.activeItemId,
27477
- collapsed: false,
27478
- itemAs: props.itemAs,
27479
- appearance,
27480
- classNames: {
27481
- ...classNames,
27482
- mobileItem: cn(classNames?.mobileItem, props.mobile?.itemClassName)
27846
+ children: items.map((item) => {
27847
+ const mobileClassNames = {
27848
+ ...classNames,
27849
+ mobileItem: cn(
27850
+ classNames?.mobileItem,
27851
+ props.mobile?.itemClassName
27852
+ )
27853
+ };
27854
+ const routeItem = props.kind === "route" ? item : void 0;
27855
+ const showRouteActions = routeItem !== void 0 && hasRenderableNode(routeItem.actions);
27856
+ if (props.kind === "route" && isDisclosureRouteItem(item)) {
27857
+ return showRouteActions ? /* @__PURE__ */ jsxs(
27858
+ "div",
27859
+ {
27860
+ "data-navigation-sidebar-item-row": item.id,
27861
+ "data-active": "false",
27862
+ className: cn(
27863
+ routeRowClassName({ active: false, appearance }),
27864
+ "shrink-0"
27865
+ ),
27866
+ children: [
27867
+ /* @__PURE__ */ jsx(
27868
+ EntryPresentation,
27869
+ {
27870
+ item,
27871
+ appearance,
27872
+ classNames: mobileClassNames
27873
+ }
27874
+ ),
27875
+ /* @__PURE__ */ jsx(
27876
+ RouteActions,
27877
+ {
27878
+ itemId: item.id,
27879
+ actions: item.actions,
27880
+ kind: "item",
27881
+ className: classNames?.itemActions,
27882
+ mobile: true
27883
+ }
27884
+ ),
27885
+ /* @__PURE__ */ jsx(
27886
+ CollapsedChildMenu,
27887
+ {
27888
+ item,
27889
+ activeChildId: props.activeChildId,
27890
+ label: props.collapsedChildMenuLabel ?? "Open submenu",
27891
+ onChildSelect: props.onChildSelect,
27892
+ classNames: mobileClassNames,
27893
+ appearance,
27894
+ triggerVariant: "disclosure",
27895
+ mobile: true
27896
+ }
27897
+ )
27898
+ ]
27899
+ },
27900
+ item.id
27901
+ ) : /* @__PURE__ */ jsx(Fragment$1, { children: /* @__PURE__ */ jsx(
27902
+ CollapsedChildMenu,
27903
+ {
27904
+ item,
27905
+ activeChildId: props.activeChildId,
27906
+ label: props.collapsedChildMenuLabel ?? "Open submenu",
27907
+ onChildSelect: props.onChildSelect,
27908
+ classNames: mobileClassNames,
27909
+ appearance,
27910
+ triggerVariant: "item",
27911
+ mobile: true
27912
+ }
27913
+ ) }, item.id);
27914
+ }
27915
+ const active = props.kind === "catalog" ? item.active ?? item.id === props.activeItemId : item.id === props.activeItemId;
27916
+ const entryButton = /* @__PURE__ */ jsx(
27917
+ EntryButton,
27918
+ {
27919
+ item,
27920
+ active,
27921
+ collapsed: false,
27922
+ embedded: showRouteActions,
27923
+ itemAs: props.itemAs,
27924
+ appearance,
27925
+ classNames: mobileClassNames,
27926
+ mobile: true,
27927
+ onSelect: props.kind === "route" && props.onItemSelect ? () => props.onItemSelect?.(item) : void 0
27928
+ }
27929
+ );
27930
+ return showRouteActions && routeItem ? /* @__PURE__ */ jsxs(
27931
+ "div",
27932
+ {
27933
+ "data-navigation-sidebar-item-row": item.id,
27934
+ "data-active": String(active),
27935
+ className: cn(
27936
+ routeRowClassName({ active, appearance }),
27937
+ "shrink-0"
27938
+ ),
27939
+ children: [
27940
+ entryButton,
27941
+ /* @__PURE__ */ jsx(
27942
+ RouteActions,
27943
+ {
27944
+ itemId: routeItem.id,
27945
+ actions: routeItem.actions,
27946
+ kind: "item",
27947
+ className: classNames?.itemActions,
27948
+ mobile: true
27949
+ }
27950
+ )
27951
+ ]
27483
27952
  },
27484
- mobile: true,
27485
- onSelect: props.kind === "route" && props.onItemSelect ? () => props.onItemSelect?.(item) : void 0
27486
- },
27487
- item.id
27488
- ))
27953
+ item.id
27954
+ ) : /* @__PURE__ */ jsx(Fragment$1, { children: entryButton }, item.id);
27955
+ })
27489
27956
  }
27490
27957
  );
27491
27958
  }