sangam-ui 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1585,21 +1585,40 @@ declare const PageFooter: React.ForwardRefExoticComponent<PageFooterProps & Reac
1585
1585
  *
1586
1586
  * Logo 36×28. Text: 12px, weight 500, line-height 16px.
1587
1587
  */
1588
- interface SideMenuItem {
1588
+ /**
1589
+ * Data for one row in a {@link SideMenuGroup}.
1590
+ * Each entry is rendered exclusively with {@link SideMenuItem}.
1591
+ */
1592
+ interface SideMenuNavItem {
1589
1593
  id: string;
1590
1594
  label: string;
1591
1595
  /** Icon for collapsed state (and expanded when iconExpanded not provided) */
1592
1596
  icon: React.ReactNode;
1593
1597
  /** Icon for expanded state. Use when icon needs different styling on white bg (e.g. Profile/avtarimg). */
1594
1598
  iconExpanded?: React.ReactNode;
1599
+ /** When the menu is expanded, show a trailing chevron (submenu affordance). */
1600
+ hasSubmenu?: boolean;
1601
+ }
1602
+ /**
1603
+ * Group wrapper config for SideMenu rows.
1604
+ * Label behavior:
1605
+ * - Expanded: shows `groupName`
1606
+ * - Collapsed: shows `--`
1607
+ * - `showGroup: false`: hides label, still renders items
1608
+ */
1609
+ interface SideMenuItemGroup {
1610
+ id: string;
1611
+ groupName: string;
1612
+ items: SideMenuNavItem[];
1613
+ showGroup?: boolean;
1595
1614
  }
1596
1615
  interface SideMenuProps extends Omit<React.HTMLAttributes<HTMLElement>, "children">, VariantProps<typeof sideMenuVariants> {
1597
1616
  /** Logo element (e.g. img 36×28) */
1598
1617
  logo: React.ReactNode;
1599
- /** Top nav items: Dashboard, Orders, Projects */
1600
- topItems: SideMenuItem[];
1601
- /** Bottom nav items: Search, Settings, Profile */
1602
- bottomItems: SideMenuItem[];
1618
+ /** Top groups of nav rows (e.g., AI Services, Compute, Storage). */
1619
+ topGroups: SideMenuItemGroup[];
1620
+ /** Bottom groups of nav rows (e.g., utilities/profile section). */
1621
+ bottomGroups: SideMenuItemGroup[];
1603
1622
  /** ID of currently selected item (controlled). Omit for uncontrolled; use defaultSelectedId for initial. */
1604
1623
  selectedId?: string;
1605
1624
  /** Initial selected ID when uncontrolled. Ignored if selectedId is provided. */
@@ -1625,6 +1644,29 @@ declare const sideMenuVariants: (props?: ({
1625
1644
  } & class_variance_authority_dist_types.ClassProp) | undefined) => string;
1626
1645
  declare const SideMenu: React.ForwardRefExoticComponent<SideMenuProps & React.RefAttributes<HTMLElement>>;
1627
1646
 
1647
+ /**
1648
+ * Single row for {@link SideMenu}: leading icon, optional label, optional submenu chevron.
1649
+ * Use inside `SideMenu` via grouped sections, or alone for layout/testing.
1650
+ */
1651
+ interface SideMenuItemProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "children"> {
1652
+ /** Visible label (hidden when `iconOnly` is true). */
1653
+ label: string;
1654
+ /** Leading icon, typically from `@esds-sangam/icons`. Parent picks collapsed vs expanded asset. */
1655
+ leadingIcon: React.ReactNode;
1656
+ /** When true, only the icon is shown (collapsed rail / design “icon only”). */
1657
+ iconOnly?: boolean;
1658
+ /** When true and not `iconOnly`, shows a trailing chevron (submenu affordance). */
1659
+ hasSubmenu?: boolean;
1660
+ /** Selected / active row styling. */
1661
+ selected?: boolean;
1662
+ /**
1663
+ * Applies hover surface without a pointer (e.g. Storybook / design previews).
1664
+ * Ignored when `selected` is true.
1665
+ */
1666
+ hovered?: boolean;
1667
+ }
1668
+ declare const SideMenuItem: React.ForwardRefExoticComponent<SideMenuItemProps & React.RefAttributes<HTMLButtonElement>>;
1669
+
1628
1670
  /**
1629
1671
  * Upload Pattern – Stage 1: File uploader
1630
1672
  *
@@ -1697,4 +1739,4 @@ type Size = "sm" | "md" | "lg";
1697
1739
  type Variant = "primary" | "secondary" | "outline" | "ghost";
1698
1740
  type ColorScheme = "primary" | "secondary" | "success" | "error" | "warning" | "info";
1699
1741
 
1700
- export { Avatar, Badge, type BaseComponentProps, Button, Card, Checkbox, Chip, type ColorScheme, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerHeaderTop, DrawerSurface, DrawerTitle, DrawerTrigger, Dropdown, DropdownMulti, type DropdownMultiProps, type DropdownOption, type DropdownProps, Input, Label, Loader, Menu, MenuContent, MenuItem, MenuMenu, MenuSeparator, MenuSub, MenuSubContent, MenuSubTrigger, MenuTrigger, PageFooter, type PageFooterAction, type PageFooterProps, PageHeader, type PageHeaderAction, type PageHeaderProps, type PageHeaderTab, Pagination, Popup, PopupContent, PopupTrigger, ProgressBar, Radio, RadioGroup, SearchField, SideMenu, type SideMenuItem, type SideMenuProps, type Size, Skeleton, Stepper, type StepperProps, type StepperStep, type StepperStepVariant, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsRoot, TabsTrigger, type TabsTriggerOption, type TabsTriggerProps, Textarea, Toast, type ToastEntryOptions, ToastProvider, Toggle, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, Upload, UploadFileItem, type UploadFileItemProps, type UploadFileItemStatus, type UploadProps, type Variant, avatarVariants, badgeVariants, buttonVariants, cardVariants, checkboxRootVariants as checkboxVariants, chipVariants, dropdownContentVariants, dropdownItemVariants, dropdownTriggerVariants, inputVariants, loaderVariants, pageHeaderVariants, radioVariants, searchFieldVariants, sideMenuVariants, skeletonVariants, tabsListVariants, tabsTriggerVariants, textareaVariants, toastVariants, toggleVariants, tooltipContentVariants, uploadBoxVariants, uploadFileItemBoxVariants, useToast };
1742
+ export { Avatar, Badge, type BaseComponentProps, Button, Card, Checkbox, Chip, type ColorScheme, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerHeaderTop, DrawerSurface, DrawerTitle, DrawerTrigger, Dropdown, DropdownMulti, type DropdownMultiProps, type DropdownOption, type DropdownProps, Input, Label, Loader, Menu, MenuContent, MenuItem, MenuMenu, MenuSeparator, MenuSub, MenuSubContent, MenuSubTrigger, MenuTrigger, PageFooter, type PageFooterAction, type PageFooterProps, PageHeader, type PageHeaderAction, type PageHeaderProps, type PageHeaderTab, Pagination, Popup, PopupContent, PopupTrigger, ProgressBar, Radio, RadioGroup, SearchField, SideMenu, SideMenuItem, type SideMenuItemGroup, type SideMenuItemProps, type SideMenuNavItem, type SideMenuProps, type Size, Skeleton, Stepper, type StepperProps, type StepperStep, type StepperStepVariant, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsRoot, TabsTrigger, type TabsTriggerOption, type TabsTriggerProps, Textarea, Toast, type ToastEntryOptions, ToastProvider, Toggle, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, Upload, UploadFileItem, type UploadFileItemProps, type UploadFileItemStatus, type UploadProps, type Variant, avatarVariants, badgeVariants, buttonVariants, cardVariants, checkboxRootVariants as checkboxVariants, chipVariants, dropdownContentVariants, dropdownItemVariants, dropdownTriggerVariants, inputVariants, loaderVariants, pageHeaderVariants, radioVariants, searchFieldVariants, sideMenuVariants, skeletonVariants, tabsListVariants, tabsTriggerVariants, textareaVariants, toastVariants, toggleVariants, tooltipContentVariants, uploadBoxVariants, uploadFileItemBoxVariants, useToast };
package/dist/index.js CHANGED
@@ -3474,12 +3474,144 @@ var PageFooter = React26.forwardRef(
3474
3474
  PageFooter.displayName = "PageFooter";
3475
3475
 
3476
3476
  // src/patterns/SideMenu.tsx
3477
- import * as React27 from "react";
3477
+ import * as React28 from "react";
3478
3478
  import { cva as cva19 } from "class-variance-authority";
3479
- import { cn as cn27 } from "@esds-sangam/utils";
3479
+ import { cn as cn28 } from "@esds-sangam/utils";
3480
3480
  import * as SelectPrimitive2 from "@radix-ui/react-select";
3481
3481
  import { ChevronDown as ChevronDown3, ChevronUp as ChevronUp2, Tickmark as Tickmark4 } from "@esds-sangam/icons";
3482
- import { Fragment as Fragment4, jsx as jsx27, jsxs as jsxs21 } from "react/jsx-runtime";
3482
+
3483
+ // src/patterns/SideMenuItem.tsx
3484
+ import * as React27 from "react";
3485
+ import { ChevronRight as ChevronRight3 } from "@esds-sangam/icons";
3486
+ import { cn as cn27 } from "@esds-sangam/utils";
3487
+ import { jsx as jsx27, jsxs as jsxs21 } from "react/jsx-runtime";
3488
+ var MENU_TEXT_STYLES = "text-xs font-medium leading-4 text-semantic-text-neutral-primary";
3489
+ var SideMenuItem = React27.forwardRef(
3490
+ ({
3491
+ label,
3492
+ leadingIcon,
3493
+ iconOnly = false,
3494
+ hasSubmenu = false,
3495
+ selected = false,
3496
+ hovered = false,
3497
+ className,
3498
+ type = "button",
3499
+ ...props
3500
+ }, ref) => {
3501
+ if (iconOnly) {
3502
+ return /* @__PURE__ */ jsx27(
3503
+ "button",
3504
+ {
3505
+ ref,
3506
+ type,
3507
+ className: cn27(
3508
+ "inline-flex w-full items-center justify-center outline-none focus-visible:ring-2 focus-visible:ring-primary-600 focus-visible:ring-offset-2",
3509
+ className
3510
+ ),
3511
+ ...props,
3512
+ children: /* @__PURE__ */ jsx27(
3513
+ "span",
3514
+ {
3515
+ className: cn27(
3516
+ "flex h-9 w-9 items-center justify-center rounded-sm [&>svg]:size-5",
3517
+ selected && "border border-semantic-border-neutral-secondary bg-semantic-background-neutral-primary shadow-elevation-bottom-sm"
3518
+ ),
3519
+ children: leadingIcon
3520
+ }
3521
+ )
3522
+ }
3523
+ );
3524
+ }
3525
+ return /* @__PURE__ */ jsx27(
3526
+ "button",
3527
+ {
3528
+ ref,
3529
+ type,
3530
+ className: cn27(
3531
+ "inline-flex w-full items-center justify-start outline-none focus-visible:ring-2 focus-visible:ring-primary-600 focus-visible:ring-offset-2",
3532
+ className
3533
+ ),
3534
+ ...props,
3535
+ children: /* @__PURE__ */ jsxs21(
3536
+ "span",
3537
+ {
3538
+ className: cn27(
3539
+ "flex h-9 min-w-0 flex-1 items-center gap-2 rounded-sm px-2",
3540
+ selected ? "mx-1 my-0.5 border border-semantic-border-neutral-secondary bg-semantic-background-neutral-primary shadow-elevation-bottom-sm" : "hover:bg-neutral-200/60",
3541
+ !selected && hovered && "bg-neutral-200/60"
3542
+ ),
3543
+ children: [
3544
+ /* @__PURE__ */ jsx27("span", { className: "shrink-0 [&>svg]:size-5", children: leadingIcon }),
3545
+ /* @__PURE__ */ jsx27("span", { className: cn27(MENU_TEXT_STYLES, "min-w-0 flex-1 truncate text-left"), children: label }),
3546
+ hasSubmenu ? /* @__PURE__ */ jsx27(
3547
+ ChevronRight3,
3548
+ {
3549
+ size: 14,
3550
+ className: "shrink-0 text-semantic-icon-neutral-tertiary",
3551
+ "aria-hidden": true
3552
+ }
3553
+ ) : null
3554
+ ]
3555
+ }
3556
+ )
3557
+ }
3558
+ );
3559
+ }
3560
+ );
3561
+ SideMenuItem.displayName = "SideMenuItem";
3562
+
3563
+ // src/patterns/SideMenu.tsx
3564
+ import { Fragment as Fragment4, jsx as jsx28, jsxs as jsxs22 } from "react/jsx-runtime";
3565
+ function SideMenuNavRow({
3566
+ item,
3567
+ isCollapsed,
3568
+ isSelected,
3569
+ onClick
3570
+ }) {
3571
+ const leadingIcon = isCollapsed && !isSelected ? item.icon : item.iconExpanded ?? item.icon;
3572
+ return /* @__PURE__ */ jsx28(
3573
+ SideMenuItem,
3574
+ {
3575
+ label: item.label,
3576
+ leadingIcon,
3577
+ iconOnly: isCollapsed,
3578
+ hasSubmenu: item.hasSubmenu ?? false,
3579
+ selected: isSelected,
3580
+ onClick
3581
+ }
3582
+ );
3583
+ }
3584
+ function SideMenuGroup({
3585
+ group,
3586
+ isCollapsed,
3587
+ selectedId,
3588
+ onItemClick,
3589
+ className
3590
+ }) {
3591
+ const showGroup = group.showGroup ?? true;
3592
+ return /* @__PURE__ */ jsxs22("div", { className: cn28("flex flex-col gap-2", className), children: [
3593
+ showGroup ? /* @__PURE__ */ jsx28(
3594
+ "div",
3595
+ {
3596
+ className: cn28(
3597
+ "text-xs font-medium leading-4 text-semantic-text-neutral-tertiary",
3598
+ isCollapsed ? "px-0 text-center" : "px-2 text-left"
3599
+ ),
3600
+ children: isCollapsed ? "--" : group.groupName
3601
+ }
3602
+ ) : null,
3603
+ group.items.map((item) => /* @__PURE__ */ jsx28(
3604
+ SideMenuNavRow,
3605
+ {
3606
+ item,
3607
+ isCollapsed,
3608
+ isSelected: selectedId === item.id,
3609
+ onClick: () => onItemClick(item.id)
3610
+ },
3611
+ item.id
3612
+ ))
3613
+ ] });
3614
+ }
3483
3615
  var sideMenuVariants = cva19(
3484
3616
  "flex flex-col self-start transition-[width] duration-200 ease-out",
3485
3617
  {
@@ -3496,14 +3628,13 @@ var sideMenuVariants = cva19(
3496
3628
  }
3497
3629
  }
3498
3630
  );
3499
- var MENU_TEXT_STYLES = "text-xs font-medium leading-4 text-semantic-text-neutral-primary";
3500
- var SideMenu = React27.forwardRef(
3631
+ var SideMenu = React28.forwardRef(
3501
3632
  ({
3502
3633
  className,
3503
3634
  variant = "default",
3504
3635
  logo,
3505
- topItems,
3506
- bottomItems,
3636
+ topGroups,
3637
+ bottomGroups,
3507
3638
  selectedId: selectedIdProp,
3508
3639
  defaultSelectedId,
3509
3640
  expanded: expandedProp,
@@ -3516,17 +3647,17 @@ var SideMenu = React27.forwardRef(
3516
3647
  onMouseLeave,
3517
3648
  ...props
3518
3649
  }, ref) => {
3519
- const [internalSelected, setInternalSelected] = React27.useState(
3650
+ const [internalSelected, setInternalSelected] = React28.useState(
3520
3651
  defaultSelectedId ?? null
3521
3652
  );
3522
3653
  const selectedId = selectedIdProp !== void 0 ? selectedIdProp : internalSelected;
3523
3654
  const isControlled = selectedIdProp !== void 0;
3524
- const [hoverExpanded, setHoverExpanded] = React27.useState(false);
3655
+ const [hoverExpanded, setHoverExpanded] = React28.useState(false);
3525
3656
  const isUncontrolledDefault = variant === "default" && expandedProp === void 0;
3526
3657
  const isExpandedByHover = isUncontrolledDefault && hoverExpanded;
3527
3658
  const effectiveExpanded = variant === "hover" || variant === "selected" || variant === "sticky" || isExpandedByHover;
3528
3659
  const isCollapsed = !effectiveExpanded;
3529
- const [isProjectOpen, setIsProjectOpen] = React27.useState(false);
3660
+ const [isProjectOpen, setIsProjectOpen] = React28.useState(false);
3530
3661
  const handleMouseEnter = (e) => {
3531
3662
  if (isUncontrolledDefault) setHoverExpanded(true);
3532
3663
  onMouseEnter?.(e);
@@ -3544,7 +3675,7 @@ var SideMenu = React27.forwardRef(
3544
3675
  { value: "project-2", label: "Project 2" },
3545
3676
  { value: "project-3", label: "Project 3" }
3546
3677
  ];
3547
- const [internalProject, setInternalProject] = React27.useState(
3678
+ const [internalProject, setInternalProject] = React28.useState(
3548
3679
  defaultProject ?? projectOptions[0]?.value ?? "project-1"
3549
3680
  );
3550
3681
  const currentProject = projectProp !== void 0 ? projectProp : internalProject;
@@ -3552,18 +3683,18 @@ var SideMenu = React27.forwardRef(
3552
3683
  if (projectProp === void 0) setInternalProject(value);
3553
3684
  onProjectChange?.(value);
3554
3685
  };
3555
- return /* @__PURE__ */ jsx27(
3686
+ return /* @__PURE__ */ jsx28(
3556
3687
  "div",
3557
3688
  {
3558
- className: cn27("relative w-12 shrink-0 overflow-visible", className),
3689
+ className: cn28("relative w-12 shrink-0 overflow-visible", className),
3559
3690
  ...props,
3560
- children: /* @__PURE__ */ jsx27(
3691
+ children: /* @__PURE__ */ jsx28(
3561
3692
  "div",
3562
3693
  {
3563
3694
  className: "absolute left-0 top-0",
3564
3695
  onMouseEnter: handleMouseEnter,
3565
3696
  onMouseLeave: handleMouseLeave,
3566
- children: /* @__PURE__ */ jsxs21(
3697
+ children: /* @__PURE__ */ jsxs22(
3567
3698
  "nav",
3568
3699
  {
3569
3700
  ref,
@@ -3574,17 +3705,17 @@ var SideMenu = React27.forwardRef(
3574
3705
  variant: isExpandedByHover && selectedId ? "selected" : isExpandedByHover ? "hover" : variant
3575
3706
  }),
3576
3707
  children: [
3577
- /* @__PURE__ */ jsx27(
3708
+ /* @__PURE__ */ jsx28(
3578
3709
  "div",
3579
3710
  {
3580
- className: cn27(
3711
+ className: cn28(
3581
3712
  "mb-4 shrink-0 flex items-center",
3582
3713
  isCollapsed ? "justify-center" : "justify-start"
3583
3714
  ),
3584
3715
  children: logo
3585
3716
  }
3586
3717
  ),
3587
- /* @__PURE__ */ jsx27("div", { className: cn27("mb-4 shrink-0", isCollapsed ? "flex justify-center" : ""), children: isCollapsed ? /* @__PURE__ */ jsx27("div", { className: "flex h-9 w-9 items-center justify-center rounded-sm", children: /* @__PURE__ */ jsx27("span", { className: "inline-flex h-6 w-6 items-center justify-center rounded-[4px] bg-blue-300 text-sm font-semibold leading-6 text-blue-600", children: "P" }) }) : /* @__PURE__ */ jsx27(
3718
+ /* @__PURE__ */ jsx28("div", { className: cn28("mb-4 shrink-0", isCollapsed ? "flex justify-center" : ""), children: isCollapsed ? /* @__PURE__ */ jsx28("div", { className: "flex h-9 w-9 items-center justify-center rounded-sm", children: /* @__PURE__ */ jsx28("span", { className: "inline-flex h-6 w-6 items-center justify-center rounded-[4px] bg-blue-300 text-sm font-semibold leading-6 text-blue-600", children: "P" }) }) : /* @__PURE__ */ jsx28(
3588
3719
  SelectPrimitive2.Root,
3589
3720
  {
3590
3721
  value: currentProject,
@@ -3594,12 +3725,12 @@ var SideMenu = React27.forwardRef(
3594
3725
  setIsProjectOpen(open);
3595
3726
  if (isUncontrolledDefault && open) setHoverExpanded(true);
3596
3727
  },
3597
- children: /* @__PURE__ */ jsxs21("div", { className: "relative", children: [
3598
- /* @__PURE__ */ jsx27("span", { className: "pointer-events-none absolute left-2 top-1/2 -translate-y-1/2 inline-flex h-6 w-6 items-center justify-center rounded-[4px] bg-blue-300 text-sm font-semibold leading-6 text-blue-600", children: "P" }),
3599
- /* @__PURE__ */ jsxs21(
3728
+ children: /* @__PURE__ */ jsxs22("div", { className: "relative", children: [
3729
+ /* @__PURE__ */ jsx28("span", { className: "pointer-events-none absolute left-2 top-1/2 -translate-y-1/2 inline-flex h-6 w-6 items-center justify-center rounded-[4px] bg-blue-300 text-sm font-semibold leading-6 text-blue-600", children: "P" }),
3730
+ /* @__PURE__ */ jsxs22(
3600
3731
  SelectPrimitive2.Trigger,
3601
3732
  {
3602
- className: cn27(
3733
+ className: cn28(
3603
3734
  "group",
3604
3735
  dropdownTriggerVariants({ error: false }),
3605
3736
  // Make room for the P icon
@@ -3610,97 +3741,41 @@ var SideMenu = React27.forwardRef(
3610
3741
  "[&>span]:block [&>span]:min-w-0 [&>span]:truncate [&>span]:whitespace-nowrap"
3611
3742
  ),
3612
3743
  children: [
3613
- /* @__PURE__ */ jsx27(SelectPrimitive2.Value, { placeholder: "Project 1" }),
3614
- /* @__PURE__ */ jsx27(SelectPrimitive2.Icon, { asChild: true, children: /* @__PURE__ */ jsxs21(Fragment4, { children: [
3615
- /* @__PURE__ */ jsx27(ChevronDown3, { size: 14, className: "shrink-0 group-data-[state=open]:hidden" }),
3616
- /* @__PURE__ */ jsx27(ChevronUp2, { size: 14, className: "hidden shrink-0 group-data-[state=open]:block" })
3744
+ /* @__PURE__ */ jsx28(SelectPrimitive2.Value, { placeholder: "Project 1" }),
3745
+ /* @__PURE__ */ jsx28(SelectPrimitive2.Icon, { asChild: true, children: /* @__PURE__ */ jsxs22(Fragment4, { children: [
3746
+ /* @__PURE__ */ jsx28(ChevronDown3, { size: 14, className: "shrink-0 group-data-[state=open]:hidden" }),
3747
+ /* @__PURE__ */ jsx28(ChevronUp2, { size: 14, className: "hidden shrink-0 group-data-[state=open]:block" })
3617
3748
  ] }) })
3618
3749
  ]
3619
3750
  }
3620
3751
  ),
3621
- /* @__PURE__ */ jsx27(SelectPrimitive2.Portal, { children: /* @__PURE__ */ jsx27(SelectPrimitive2.Content, { className: cn27(dropdownContentVariants()), position: "popper", sideOffset: 6, children: /* @__PURE__ */ jsx27(SelectPrimitive2.Viewport, { children: projectOptions.map((opt) => /* @__PURE__ */ jsxs21(SelectPrimitive2.Item, { value: opt.value, disabled: opt.disabled, className: cn27(dropdownItemVariants()), children: [
3622
- /* @__PURE__ */ jsx27(SelectPrimitive2.ItemText, { children: opt.label }),
3623
- /* @__PURE__ */ jsx27(SelectPrimitive2.ItemIndicator, { className: "absolute right-2 inline-flex items-center justify-center", children: /* @__PURE__ */ jsx27(Tickmark4, { size: 14, "aria-hidden": "true" }) })
3752
+ /* @__PURE__ */ jsx28(SelectPrimitive2.Portal, { children: /* @__PURE__ */ jsx28(SelectPrimitive2.Content, { className: cn28(dropdownContentVariants()), position: "popper", sideOffset: 6, children: /* @__PURE__ */ jsx28(SelectPrimitive2.Viewport, { children: projectOptions.map((opt) => /* @__PURE__ */ jsxs22(SelectPrimitive2.Item, { value: opt.value, disabled: opt.disabled, className: cn28(dropdownItemVariants()), children: [
3753
+ /* @__PURE__ */ jsx28(SelectPrimitive2.ItemText, { children: opt.label }),
3754
+ /* @__PURE__ */ jsx28(SelectPrimitive2.ItemIndicator, { className: "absolute right-2 inline-flex items-center justify-center", children: /* @__PURE__ */ jsx28(Tickmark4, { size: 14, "aria-hidden": "true" }) })
3624
3755
  ] }, opt.value)) }) }) })
3625
3756
  ] })
3626
3757
  }
3627
3758
  ) }),
3628
- /* @__PURE__ */ jsx27("div", { className: "flex flex-1 flex-col gap-2", children: topItems.map((item) => {
3629
- const isSelected = selectedId === item.id;
3630
- const itemIcon = isCollapsed && !isSelected ? item.icon : item.iconExpanded ?? item.icon;
3631
- return /* @__PURE__ */ jsx27(
3632
- "button",
3633
- {
3634
- type: "button",
3635
- onClick: () => handleItemClick(item.id),
3636
- className: cn27(
3637
- "inline-flex w-full items-center outline-none focus-visible:ring-2 focus-visible:ring-primary-600 focus-visible:ring-offset-2",
3638
- isCollapsed ? "justify-center" : "justify-start"
3639
- ),
3640
- children: isCollapsed ? /* @__PURE__ */ jsx27(
3641
- "span",
3642
- {
3643
- className: cn27(
3644
- "flex h-9 w-9 items-center justify-center rounded-sm [&>svg]:size-5",
3645
- isSelected && "border border-semantic-border-neutral-secondary bg-semantic-background-neutral-primary shadow-elevation-bottom-sm"
3646
- ),
3647
- children: itemIcon
3648
- }
3649
- ) : /* @__PURE__ */ jsxs21(
3650
- "span",
3651
- {
3652
- className: cn27(
3653
- "flex h-9 min-w-0 flex-1 items-center gap-2 rounded-sm px-2",
3654
- isSelected ? "mx-1 my-0.5 border border-semantic-border-neutral-secondary bg-semantic-background-neutral-primary shadow-elevation-bottom-sm" : "hover:bg-neutral-200/60"
3655
- ),
3656
- children: [
3657
- /* @__PURE__ */ jsx27("span", { className: "shrink-0 [&>svg]:size-5", children: itemIcon }),
3658
- /* @__PURE__ */ jsx27("span", { className: cn27(MENU_TEXT_STYLES, "truncate"), children: item.label })
3659
- ]
3660
- }
3661
- )
3662
- },
3663
- item.id
3664
- );
3665
- }) }),
3666
- /* @__PURE__ */ jsx27("div", { className: "flex flex-col gap-2 pt-4", children: bottomItems.map((item) => {
3667
- const isSelected = selectedId === item.id;
3668
- const itemIcon = isCollapsed && !isSelected ? item.icon : item.iconExpanded ?? item.icon;
3669
- return /* @__PURE__ */ jsx27(
3670
- "button",
3671
- {
3672
- type: "button",
3673
- onClick: () => handleItemClick(item.id),
3674
- className: cn27(
3675
- "inline-flex w-full items-center outline-none focus-visible:ring-2 focus-visible:ring-primary-600 focus-visible:ring-offset-2",
3676
- isCollapsed ? "justify-center" : "justify-start"
3677
- ),
3678
- children: isCollapsed ? /* @__PURE__ */ jsx27(
3679
- "span",
3680
- {
3681
- className: cn27(
3682
- "flex h-9 w-9 items-center justify-center rounded-sm [&>svg]:size-5",
3683
- isSelected && "border border-semantic-border-neutral-secondary bg-semantic-background-neutral-primary shadow-elevation-bottom-sm"
3684
- ),
3685
- children: itemIcon
3686
- }
3687
- ) : /* @__PURE__ */ jsxs21(
3688
- "span",
3689
- {
3690
- className: cn27(
3691
- "flex h-9 min-w-0 flex-1 items-center gap-2 rounded-sm px-2",
3692
- isSelected ? "mx-1 my-0.5 border border-semantic-border-neutral-secondary bg-semantic-background-neutral-primary shadow-elevation-bottom-sm" : "hover:bg-neutral-200/60"
3693
- ),
3694
- children: [
3695
- /* @__PURE__ */ jsx27("span", { className: "shrink-0 [&>svg]:size-5", children: itemIcon }),
3696
- /* @__PURE__ */ jsx27("span", { className: cn27(MENU_TEXT_STYLES, "truncate"), children: item.label })
3697
- ]
3698
- }
3699
- )
3700
- },
3701
- item.id
3702
- );
3703
- }) })
3759
+ /* @__PURE__ */ jsx28("div", { className: "flex flex-1 flex-col gap-4", children: topGroups.map((group) => /* @__PURE__ */ jsx28(
3760
+ SideMenuGroup,
3761
+ {
3762
+ group,
3763
+ isCollapsed,
3764
+ selectedId,
3765
+ onItemClick: handleItemClick
3766
+ },
3767
+ group.id
3768
+ )) }),
3769
+ /* @__PURE__ */ jsx28("div", { className: "flex flex-col gap-4 pt-4", children: bottomGroups.map((group) => /* @__PURE__ */ jsx28(
3770
+ SideMenuGroup,
3771
+ {
3772
+ group,
3773
+ isCollapsed,
3774
+ selectedId,
3775
+ onItemClick: handleItemClick
3776
+ },
3777
+ group.id
3778
+ )) })
3704
3779
  ]
3705
3780
  }
3706
3781
  )
@@ -3713,12 +3788,12 @@ var SideMenu = React27.forwardRef(
3713
3788
  SideMenu.displayName = "SideMenu";
3714
3789
 
3715
3790
  // src/patterns/Upload.tsx
3716
- import * as React28 from "react";
3791
+ import * as React29 from "react";
3717
3792
  import { cva as cva20 } from "class-variance-authority";
3718
3793
  import { colors } from "@esds-sangam/tokens";
3719
- import { cn as cn28 } from "@esds-sangam/utils";
3794
+ import { cn as cn29 } from "@esds-sangam/utils";
3720
3795
  import { CloudUpload, DocumentPdf, Close as Close11, Delete, Retry, TickmarkFilled as TickmarkFilled6 } from "@esds-sangam/icons";
3721
- import { Fragment as Fragment5, jsx as jsx28, jsxs as jsxs22 } from "react/jsx-runtime";
3796
+ import { Fragment as Fragment5, jsx as jsx29, jsxs as jsxs23 } from "react/jsx-runtime";
3722
3797
  var uploadBoxVariants = cva20([
3723
3798
  "relative flex flex-col items-center justify-center w-full",
3724
3799
  "rounded-sm border border-dashed border-semantic-border-neutral-primary hover:border-semantic-border-neutralInverse-primary",
@@ -3733,7 +3808,7 @@ function formatFileSize(bytes) {
3733
3808
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
3734
3809
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
3735
3810
  }
3736
- var Upload = React28.forwardRef(
3811
+ var Upload = React29.forwardRef(
3737
3812
  ({
3738
3813
  className,
3739
3814
  label,
@@ -3744,9 +3819,9 @@ var Upload = React28.forwardRef(
3744
3819
  helperText = "PNG, JPG, PDF, EXCEL (max size)",
3745
3820
  ...props
3746
3821
  }, ref) => {
3747
- const inputRef = React28.useRef(null);
3748
- const [fileEntries, setFileEntries] = React28.useState([]);
3749
- const progressIntervalRef = React28.useRef(null);
3822
+ const inputRef = React29.useRef(null);
3823
+ const [fileEntries, setFileEntries] = React29.useState([]);
3824
+ const progressIntervalRef = React29.useRef(null);
3750
3825
  const handleClick = () => {
3751
3826
  inputRef.current?.click();
3752
3827
  };
@@ -3774,7 +3849,7 @@ var Upload = React28.forwardRef(
3774
3849
  )
3775
3850
  );
3776
3851
  };
3777
- React28.useEffect(() => {
3852
+ React29.useEffect(() => {
3778
3853
  const uploading = fileEntries.filter((e) => e.status === "uploading");
3779
3854
  if (uploading.length === 0) return;
3780
3855
  const steps = [10, 50, 100];
@@ -3813,18 +3888,18 @@ var Upload = React28.forwardRef(
3813
3888
  const handleDragOver = (event) => {
3814
3889
  event.preventDefault();
3815
3890
  };
3816
- return /* @__PURE__ */ jsxs22(
3891
+ return /* @__PURE__ */ jsxs23(
3817
3892
  "div",
3818
3893
  {
3819
3894
  ref,
3820
- className: cn28("flex w-full min-w-[328px] flex-col gap-2", className),
3895
+ className: cn29("flex w-full min-w-[328px] flex-col gap-2", className),
3821
3896
  ...props,
3822
3897
  children: [
3823
- /* @__PURE__ */ jsxs22("label", { className: "text-xs font-medium leading-4 text-semantic-text-neutral-primary", children: [
3898
+ /* @__PURE__ */ jsxs23("label", { className: "text-xs font-medium leading-4 text-semantic-text-neutral-primary", children: [
3824
3899
  label,
3825
- required && /* @__PURE__ */ jsx28("span", { className: "text-semantic-text-semantic-error-subtle ml-0.5", children: "*" })
3900
+ required && /* @__PURE__ */ jsx29("span", { className: "text-semantic-text-semantic-error-subtle ml-0.5", children: "*" })
3826
3901
  ] }),
3827
- /* @__PURE__ */ jsxs22(
3902
+ /* @__PURE__ */ jsxs23(
3828
3903
  "div",
3829
3904
  {
3830
3905
  role: "button",
@@ -3838,13 +3913,13 @@ var Upload = React28.forwardRef(
3838
3913
  },
3839
3914
  onDrop: handleDrop,
3840
3915
  onDragOver: handleDragOver,
3841
- className: cn28(
3916
+ className: cn29(
3842
3917
  uploadBoxVariants(),
3843
3918
  // Padding: top/bottom 20px (py-5), left/right 12px (px-3)
3844
3919
  "py-5 px-3"
3845
3920
  ),
3846
3921
  children: [
3847
- /* @__PURE__ */ jsx28(
3922
+ /* @__PURE__ */ jsx29(
3848
3923
  "input",
3849
3924
  {
3850
3925
  ref: inputRef,
@@ -3857,12 +3932,12 @@ var Upload = React28.forwardRef(
3857
3932
  "aria-hidden": "true"
3858
3933
  }
3859
3934
  ),
3860
- /* @__PURE__ */ jsxs22("div", { className: "relative z-0 flex flex-col items-center gap-2 text-center pointer-events-none", children: [
3861
- /* @__PURE__ */ jsx28("div", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-semantic-background-semantic-info-subtle text-semantic-icon-semantic-info-subtle", children: /* @__PURE__ */ jsx28(CloudUpload, { size: 20, color: colors.blue["600"], "aria-hidden": "true" }) }),
3862
- /* @__PURE__ */ jsxs22("div", { className: "flex flex-col gap-1", children: [
3863
- /* @__PURE__ */ jsxs22("p", { className: "text-sm leading-6 text-semantic-text-neutral-primary", children: [
3864
- /* @__PURE__ */ jsx28("span", { className: "font-normal", children: "Drop your files here or " }),
3865
- /* @__PURE__ */ jsx28(
3935
+ /* @__PURE__ */ jsxs23("div", { className: "relative z-0 flex flex-col items-center gap-2 text-center pointer-events-none", children: [
3936
+ /* @__PURE__ */ jsx29("div", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-semantic-background-semantic-info-subtle text-semantic-icon-semantic-info-subtle", children: /* @__PURE__ */ jsx29(CloudUpload, { size: 20, color: colors.blue["600"], "aria-hidden": "true" }) }),
3937
+ /* @__PURE__ */ jsxs23("div", { className: "flex flex-col gap-1", children: [
3938
+ /* @__PURE__ */ jsxs23("p", { className: "text-sm leading-6 text-semantic-text-neutral-primary", children: [
3939
+ /* @__PURE__ */ jsx29("span", { className: "font-normal", children: "Drop your files here or " }),
3940
+ /* @__PURE__ */ jsx29(
3866
3941
  Button,
3867
3942
  {
3868
3943
  type: "button",
@@ -3877,13 +3952,13 @@ var Upload = React28.forwardRef(
3877
3952
  }
3878
3953
  )
3879
3954
  ] }),
3880
- /* @__PURE__ */ jsx28("p", { className: "text-xs font-normal leading-4 text-semantic-text-neutral-tertiary", children: helperText })
3955
+ /* @__PURE__ */ jsx29("p", { className: "text-xs font-normal leading-4 text-semantic-text-neutral-tertiary", children: helperText })
3881
3956
  ] })
3882
3957
  ] })
3883
3958
  ]
3884
3959
  }
3885
3960
  ),
3886
- fileEntries.length > 0 && /* @__PURE__ */ jsx28("div", { className: "flex flex-col gap-3", children: fileEntries.map((entry) => /* @__PURE__ */ jsx28(
3961
+ fileEntries.length > 0 && /* @__PURE__ */ jsx29("div", { className: "flex flex-col gap-3", children: fileEntries.map((entry) => /* @__PURE__ */ jsx29(
3887
3962
  UploadFileItem,
3888
3963
  {
3889
3964
  status: entry.status,
@@ -3917,7 +3992,7 @@ var uploadFileItemBoxVariants = cva20(
3917
3992
  }
3918
3993
  }
3919
3994
  );
3920
- var UploadFileItem = React28.forwardRef(
3995
+ var UploadFileItem = React29.forwardRef(
3921
3996
  ({
3922
3997
  className,
3923
3998
  status = "uploading",
@@ -3931,72 +4006,72 @@ var UploadFileItem = React28.forwardRef(
3931
4006
  }, ref) => {
3932
4007
  const progressValue = Math.min(100, Math.max(0, progress));
3933
4008
  const showProgress = status !== "failed";
3934
- return /* @__PURE__ */ jsxs22(
4009
+ return /* @__PURE__ */ jsxs23(
3935
4010
  "div",
3936
4011
  {
3937
4012
  ref,
3938
4013
  role: "listitem",
3939
- className: cn28(uploadFileItemBoxVariants({ status }), className),
4014
+ className: cn29(uploadFileItemBoxVariants({ status }), className),
3940
4015
  ...props,
3941
4016
  children: [
3942
- /* @__PURE__ */ jsxs22("div", { className: "flex min-w-0 flex-1 gap-3", children: [
3943
- /* @__PURE__ */ jsxs22("div", { className: "flex flex-1 min-w-0 gap-2", children: [
3944
- /* @__PURE__ */ jsx28(DocumentPdf, { size: 24, className: "shrink-0 text-semantic-text-neutral-primary", "aria-hidden": true }),
3945
- /* @__PURE__ */ jsxs22("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
3946
- /* @__PURE__ */ jsx28("p", { className: "truncate text-sm font-medium leading-6 text-semantic-text-neutral-primary", children: fileName }),
3947
- status === "failed" ? /* @__PURE__ */ jsx28("p", { className: "text-xs font-normal leading-4 text-semantic-text-semantic-error-subtle", children: "Upload failed" }) : fileSize ? /* @__PURE__ */ jsx28("p", { className: "text-xs font-normal leading-4 text-semantic-text-neutral-tertiary", children: fileSize }) : null
4017
+ /* @__PURE__ */ jsxs23("div", { className: "flex min-w-0 flex-1 gap-3", children: [
4018
+ /* @__PURE__ */ jsxs23("div", { className: "flex flex-1 min-w-0 gap-2", children: [
4019
+ /* @__PURE__ */ jsx29(DocumentPdf, { size: 24, className: "shrink-0 text-semantic-text-neutral-primary", "aria-hidden": true }),
4020
+ /* @__PURE__ */ jsxs23("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
4021
+ /* @__PURE__ */ jsx29("p", { className: "truncate text-sm font-medium leading-6 text-semantic-text-neutral-primary", children: fileName }),
4022
+ status === "failed" ? /* @__PURE__ */ jsx29("p", { className: "text-xs font-normal leading-4 text-semantic-text-semantic-error-subtle", children: "Upload failed" }) : fileSize ? /* @__PURE__ */ jsx29("p", { className: "text-xs font-normal leading-4 text-semantic-text-neutral-tertiary", children: fileSize }) : null
3948
4023
  ] })
3949
4024
  ] }),
3950
- /* @__PURE__ */ jsxs22("div", { className: "flex shrink-0 flex-col items-end justify-center", children: [
3951
- status === "uploading" && /* @__PURE__ */ jsx28(
4025
+ /* @__PURE__ */ jsxs23("div", { className: "flex shrink-0 flex-col items-end justify-center", children: [
4026
+ status === "uploading" && /* @__PURE__ */ jsx29(
3952
4027
  "button",
3953
4028
  {
3954
4029
  type: "button",
3955
4030
  onClick: onCancel,
3956
4031
  className: "inline-flex items-center justify-center rounded p-0 text-semantic-text-neutral-primary hover:opacity-semantic-hover focus:outline-none focus-visible:ring-2 focus-visible:ring-semantic-border-neutralInverse-primary focus-visible:ring-offset-1",
3957
4032
  "aria-label": "Cancel upload",
3958
- children: /* @__PURE__ */ jsx28(Close11, { size: 20, "aria-hidden": true })
4033
+ children: /* @__PURE__ */ jsx29(Close11, { size: 20, "aria-hidden": true })
3959
4034
  }
3960
4035
  ),
3961
- status === "complete" && /* @__PURE__ */ jsx28(Fragment5, { children: /* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-4", children: [
3962
- /* @__PURE__ */ jsx28(TickmarkFilled6, { size: 20, className: "!text-semantic-icon-semantic-success-subtle shrink-0", "aria-hidden": true }),
3963
- /* @__PURE__ */ jsx28(
4036
+ status === "complete" && /* @__PURE__ */ jsx29(Fragment5, { children: /* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-4", children: [
4037
+ /* @__PURE__ */ jsx29(TickmarkFilled6, { size: 20, className: "!text-semantic-icon-semantic-success-subtle shrink-0", "aria-hidden": true }),
4038
+ /* @__PURE__ */ jsx29(
3964
4039
  "button",
3965
4040
  {
3966
4041
  type: "button",
3967
4042
  onClick: onDelete,
3968
4043
  className: "inline-flex items-center justify-center rounded p-0 text-semantic-text-neutral-primary hover:opacity-semantic-hover focus:outline-none focus-visible:ring-2 focus-visible:ring-semantic-border-neutralInverse-primary focus-visible:ring-offset-1",
3969
4044
  "aria-label": "Delete file",
3970
- children: /* @__PURE__ */ jsx28(Delete, { size: 20, "aria-hidden": true })
4045
+ children: /* @__PURE__ */ jsx29(Delete, { size: 20, "aria-hidden": true })
3971
4046
  }
3972
4047
  )
3973
4048
  ] }) }),
3974
- status === "failed" && /* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-4", children: [
3975
- /* @__PURE__ */ jsx28(
4049
+ status === "failed" && /* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-4", children: [
4050
+ /* @__PURE__ */ jsx29(
3976
4051
  "button",
3977
4052
  {
3978
4053
  type: "button",
3979
4054
  onClick: onRetry,
3980
4055
  className: "inline-flex items-center justify-center rounded p-0 text-semantic-text-neutral-primary hover:opacity-semantic-hover focus:outline-none focus-visible:ring-2 focus-visible:ring-semantic-border-neutralInverse-primary focus-visible:ring-offset-1",
3981
4056
  "aria-label": "Retry upload",
3982
- children: /* @__PURE__ */ jsx28(Retry, { size: 20, "aria-hidden": true })
4057
+ children: /* @__PURE__ */ jsx29(Retry, { size: 20, "aria-hidden": true })
3983
4058
  }
3984
4059
  ),
3985
- /* @__PURE__ */ jsx28(
4060
+ /* @__PURE__ */ jsx29(
3986
4061
  "button",
3987
4062
  {
3988
4063
  type: "button",
3989
4064
  onClick: onDelete,
3990
4065
  className: "inline-flex items-center justify-center rounded p-0 text-semantic-text-neutral-primary hover:opacity-semantic-hover focus:outline-none focus-visible:ring-2 focus-visible:ring-semantic-border-neutralInverse-primary focus-visible:ring-offset-1",
3991
4066
  "aria-label": "Delete file",
3992
- children: /* @__PURE__ */ jsx28(Delete, { size: 20, "aria-hidden": true })
4067
+ children: /* @__PURE__ */ jsx29(Delete, { size: 20, "aria-hidden": true })
3993
4068
  }
3994
4069
  )
3995
4070
  ] })
3996
4071
  ] })
3997
4072
  ] }),
3998
- showProgress && /* @__PURE__ */ jsxs22("div", { className: "flex w-full items-center gap-sm", children: [
3999
- /* @__PURE__ */ jsx28(
4073
+ showProgress && /* @__PURE__ */ jsxs23("div", { className: "flex w-full items-center gap-sm", children: [
4074
+ /* @__PURE__ */ jsx29(
4000
4075
  ProgressBar,
4001
4076
  {
4002
4077
  value: progressValue,
@@ -4005,7 +4080,7 @@ var UploadFileItem = React28.forwardRef(
4005
4080
  "aria-label": `Upload progress ${progressValue}%`
4006
4081
  }
4007
4082
  ),
4008
- /* @__PURE__ */ jsxs22("span", { className: "text-xs font-normal leading-4 text-semantic-text-neutral-tertiary shrink-0 text-right", children: [
4083
+ /* @__PURE__ */ jsxs23("span", { className: "text-xs font-normal leading-4 text-semantic-text-neutral-tertiary shrink-0 text-right", children: [
4009
4084
  progressValue,
4010
4085
  "%"
4011
4086
  ] })
@@ -4058,6 +4133,7 @@ export {
4058
4133
  RadioGroup,
4059
4134
  SearchField,
4060
4135
  SideMenu,
4136
+ SideMenuItem,
4061
4137
  Skeleton,
4062
4138
  Stepper,
4063
4139
  Table,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sangam-ui",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",