@caratlogic/design-system 0.1.5 → 0.1.7

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
@@ -1,7 +1,7 @@
1
1
  export { cn } from './lib/utils';
2
2
  export * from './lib/badge';
3
3
  export * from './lib/notify';
4
- export { releaseStaleOverlayBodyLock } from './lib/overlayBodyLock';
4
+ export { releaseStaleOverlayBodyLock, watchOverlayBodyLock, } from './lib/overlayBodyLock';
5
5
  export * from './components/atoms/ActionButtonWithTooltip';
6
6
  export * from './components/atoms/Alert';
7
7
  export * from './components/atoms/Badge';
@@ -75,6 +75,7 @@ export * from './components/molecules/SlideOut';
75
75
  export * from './components/molecules/SpecList';
76
76
  export * from './components/molecules/TablePagination';
77
77
  export * from './components/molecules/TagsInput';
78
+ export * from './components/molecules/TimePicker';
78
79
  export * from './components/molecules/WarehouseComboBox';
79
80
  export * from './components/molecules/ZoomSplitViewer';
80
81
  export * from './components/organisms/AdvancedSearchDrawer';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.esm.js CHANGED
@@ -4345,13 +4345,19 @@ const notify = {
4345
4345
  promise: toast.promise.bind(toast),
4346
4346
  };
4347
4347
 
4348
+ /**
4349
+ * Only overlays that actually lock `<body>` count. `[data-radix-popper-content-wrapper]`
4350
+ * is deliberately absent: a tooltip renders into one, so including it made an
4351
+ * open tooltip — say, on the Edit button you just clicked — look like a live
4352
+ * modal and suppress the release, leaving the page unclickable. Modal poppers
4353
+ * (menus, selects) still match through their roles.
4354
+ */
4348
4355
  const OVERLAY_CONTENT_SELECTOR = [
4349
4356
  '[data-slot="dialog-content"]',
4350
4357
  '[role="dialog"]',
4351
4358
  '[role="alertdialog"]',
4352
4359
  '[role="menu"]',
4353
4360
  '[role="listbox"]',
4354
- '[data-radix-popper-content-wrapper]',
4355
4361
  ].join(',');
4356
4362
  /**
4357
4363
  * `@radix-ui/react-dismissable-layer` keeps the value it must restore to
@@ -4363,6 +4369,36 @@ const OVERLAY_CONTENT_SELECTOR = [
4363
4369
  * Clearing is only safe once nothing is on screen, so this is a no-op while any
4364
4370
  * overlay content is still mounted.
4365
4371
  */
4372
+ /**
4373
+ * Watches `<body>` and clears a lock that outlives every overlay.
4374
+ *
4375
+ * The per-overlay hooks handle the normal close path; this is the backstop for
4376
+ * the ones they cannot see — an overlay unmounted by a route change, a layer
4377
+ * combination nobody anticipated, a third-party popup. Returns a disposer.
4378
+ *
4379
+ * Cheap: an attribute-scoped MutationObserver that only reacts when something
4380
+ * actually writes `style` or `data-scroll-locked` on `<body>`.
4381
+ */
4382
+ function watchOverlayBodyLock() {
4383
+ if (typeof document === 'undefined')
4384
+ return () => { };
4385
+ let timer;
4386
+ const check = () => {
4387
+ window.clearTimeout(timer);
4388
+ // Outlive the exit animation, so a legitimately-open overlay is still
4389
+ // mounted when `releaseStaleOverlayBodyLock` looks for it.
4390
+ timer = window.setTimeout(() => releaseStaleOverlayBodyLock(), 500);
4391
+ };
4392
+ const observer = new MutationObserver(check);
4393
+ observer.observe(document.body, {
4394
+ attributes: true,
4395
+ attributeFilter: ['style', 'data-scroll-locked'],
4396
+ });
4397
+ return () => {
4398
+ window.clearTimeout(timer);
4399
+ observer.disconnect();
4400
+ };
4401
+ }
4366
4402
  function releaseStaleOverlayBodyLock() {
4367
4403
  if (typeof document === 'undefined')
4368
4404
  return;
@@ -12093,6 +12129,9 @@ function useRefundToWallet(open) {
12093
12129
 
12094
12130
  const ConfirmDialog = ({ open, onOpenChange, title, description, info, confirmLabel = 'Yes', cancelLabel = 'Cancel', confirmVariant = 'default', icon: Icon = Trash2, disabled = false, onConfirm, onCancel, }) => {
12095
12131
  const { refundToWallet, setRefundToWallet } = useRefundToWallet(open);
12132
+ // Opened from a modal menu row, two dismissable layers overlap and Radix
12133
+ // restores `pointer-events: none` to <body> on close, freezing the page.
12134
+ useOverlayBodyLockRelease(open);
12096
12135
  return (jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: jsxs(DialogContent, { className: "gap-3 p-5", children: [jsxs(DialogHeader, { className: "gap-1.5", children: [jsxs(DialogTitle, { className: "flex items-center gap-2", children: [confirmVariant === 'destructive' && (jsx("span", { className: "inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-destructive/10", children: jsx(Icon, { className: "h-3.5 w-3.5 text-destructive" }) })), jsx("span", { children: title })] }), description && jsx(DialogDescription, { children: description }), info && (jsx(InfoCard, { info: info, title: title, refundToWallet: refundToWallet, onRefundChange: setRefundToWallet }))] }), jsxs(DialogFooter, { className: "mt-1", children: [jsx(Button$1, { variant: "outline", onClick: () => {
12097
12136
  onCancel?.();
12098
12137
  onOpenChange(false);
@@ -28576,10 +28615,12 @@ const SIZE_WIDTH = {
28576
28615
  */
28577
28616
  function Modal({ open, onClose, title, icon, children, footer, size = 'md', hideCloseButton = false, className, bodyClassName, }) {
28578
28617
  const maxWidth = SIZE_WIDTH[size];
28618
+ // See ConfirmDialog: overlapping dismissable layers leak the <body> lock.
28619
+ useOverlayBodyLockRelease(open);
28579
28620
  return (jsx(Dialog, { open: open, onOpenChange: (o) => !o && onClose(), children: jsxs(DialogContent, { hideCloseButton: true, style: {
28580
28621
  width: `min(${maxWidth}px, calc(100vw - 2rem))`,
28581
28622
  maxWidth: `${maxWidth}px`,
28582
- }, className: cn('flex max-h-[calc(100vh-2rem)] flex-col gap-0 overflow-hidden p-0 sm:max-w-none', className), children: [jsxs("header", { className: "flex items-center justify-between gap-4 border-b border-border px-6 py-4", children: [jsx(DialogPrimitive.Title, { asChild: true, children: jsxs("div", { className: "flex items-center gap-2 text-base font-medium text-foreground", children: [icon, jsx("span", { children: title })] }) }), !hideCloseButton && (jsx(DialogPrimitive.Close, { "aria-label": "Close", className: "inline-flex h-8 w-8 items-center justify-center rounded-md text-label transition-colors hover:bg-gray-100 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30", children: jsx(X$2, { className: "h-4 w-4" }) }))] }), jsx("div", { className: cn('flex-1 overflow-y-auto', bodyClassName), children: children }), footer && (jsx("footer", { className: "flex items-center justify-end gap-3 border-t border-border px-6 py-4", children: footer }))] }) }));
28623
+ }, className: cn('flex max-h-[calc(100vh-2rem)] flex-col gap-0 overflow-hidden p-0 sm:max-w-none', className), children: [jsxs("header", { className: "flex items-center justify-between gap-4 border-b border-border px-6 py-4", children: [jsx(DialogPrimitive.Title, { asChild: true, children: jsxs("div", { className: "flex items-center gap-2 text-base font-medium text-foreground", children: [icon, jsx("span", { children: title })] }) }), !hideCloseButton && (jsx(DialogPrimitive.Close, { "aria-label": "Close", className: "inline-flex h-8 w-8 items-center justify-center rounded-md text-label transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30", children: jsx(X$2, { className: "h-4 w-4" }) }))] }), jsx("div", { className: cn('flex-1 overflow-y-auto', bodyClassName), children: children }), footer && (jsx("footer", { className: "flex items-center justify-end gap-3 border-t border-border px-6 py-4", children: footer }))] }) }));
28583
28624
  }
28584
28625
 
28585
28626
  /**
@@ -57666,6 +57707,158 @@ function TagsInput({ value, onChange, placeholder, helperText, uppercase = false
57666
57707
  return (jsxs("div", { className: "w-full", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx(Input, { value: input, onChange: onInputChange, onKeyDown: onInputKeyDown, placeholder: placeholder || 'Type and Press Enter', className: "flex-1 bg-formInput" }), jsxs(Button$1, { type: "button", onClick: addTag, disabled: addDisabled, className: "h-9 gap-1.5 disabled:opacity-50", children: [jsx(Plus, { className: "h-4 w-4" }), "Add"] })] }), helperText && (jsx("p", { className: "mt-1.5 text-xs text-muted-foreground", children: helperText })), value.length > 0 && (jsx("div", { className: "mt-3 flex flex-wrap gap-2", children: value.map((tag, idx) => (jsxs("span", { className: "inline-flex items-center gap-1.5 rounded-md border border-border bg-secondary px-2 py-1 text-xs font-medium text-foreground", children: [tag, jsx("button", { type: "button", onClick: () => removeTag(idx), "aria-label": `Remove ${tag}`, className: "cursor-pointer text-muted-foreground transition-colors hover:text-foreground", children: jsx(X$2, { className: "h-3 w-3" }) })] }, idx))) }))] }));
57667
57708
  }
57668
57709
 
57710
+ /**
57711
+ * One scrolling column of the picker (hours, minutes or period). The selected
57712
+ * row is scrolled into view whenever it changes, so reopening the popover lands
57713
+ * on the current value instead of at midnight.
57714
+ */
57715
+ function TimeColumn({ label, options, selected, onSelect, }) {
57716
+ const listRef = useRef(null);
57717
+ const activeRef = useRef(null);
57718
+ useEffect(() => {
57719
+ const list = listRef.current;
57720
+ const active = activeRef.current;
57721
+ if (!list || !active)
57722
+ return;
57723
+ // `scrollIntoView` would scroll the page as well as the column.
57724
+ list.scrollTop =
57725
+ active.offsetTop - list.clientHeight / 2 + active.clientHeight / 2;
57726
+ }, [selected]);
57727
+ return (jsxs("div", { className: "flex min-w-0 flex-col", children: [jsx("span", { className: "px-1 pb-1.5 text-center text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: label }), jsx("div", { ref: listRef, role: "listbox", "aria-label": label, className: "scrollbar-thin h-[184px] overflow-y-auto px-1", children: jsx("div", { className: "flex flex-col gap-0.5", children: options.map((option) => {
57728
+ const isSelected = option.value === selected;
57729
+ return (jsx("button", { ref: isSelected ? activeRef : undefined, type: "button", role: "option", "aria-selected": isSelected, onClick: () => onSelect(option.value), className: cn('rounded-md px-3 py-1.5 text-center text-sm tabular-nums transition-colors', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring', isSelected
57730
+ ? 'bg-primary text-primary-foreground'
57731
+ : 'text-foreground hover:bg-muted'), children: option.label }, option.value));
57732
+ }) }) })] }));
57733
+ }
57734
+
57735
+ const TIME_PATTERN = /^(\d{1,2}):(\d{2})(?::\d{2})?$/;
57736
+ const pad = (n) => String(n).padStart(2, '0');
57737
+ /** Reads `HH:mm` / `HH:mm:ss`. Returns null for anything out of range. */
57738
+ function parseTimeValue(value) {
57739
+ const match = TIME_PATTERN.exec((value ?? '').trim());
57740
+ if (!match)
57741
+ return null;
57742
+ const hour = Number(match[1]);
57743
+ const minute = Number(match[2]);
57744
+ if (hour > 23 || minute > 59)
57745
+ return null;
57746
+ return { hour, minute };
57747
+ }
57748
+ /** The stored form — always 24-hour, whatever the display format. */
57749
+ function toTimeValue(hour, minute) {
57750
+ return `${pad(hour)}:${pad(minute)}`;
57751
+ }
57752
+ function formatTimeDisplay(time, use24Hour) {
57753
+ if (use24Hour)
57754
+ return toTimeValue(time.hour, time.minute);
57755
+ // 0 and 12 both map to 12 — midnight is 12 AM, noon is 12 PM.
57756
+ const hour12 = time.hour % 12 === 0 ? 12 : time.hour % 12;
57757
+ const period = time.hour < 12 ? 'AM' : 'PM';
57758
+ return `${pad(hour12)}:${pad(time.minute)} ${period}`;
57759
+ }
57760
+ function buildHourOptions(use24Hour) {
57761
+ if (use24Hour) {
57762
+ return Array.from({ length: 24 }, (_, hour) => ({
57763
+ value: hour,
57764
+ label: pad(hour),
57765
+ }));
57766
+ }
57767
+ // 12 first, matching how a clock reads.
57768
+ return Array.from({ length: 12 }, (_, index) => {
57769
+ const hour12 = index === 0 ? 12 : index;
57770
+ return { value: hour12, label: pad(hour12) };
57771
+ });
57772
+ }
57773
+ function buildMinuteOptions(step) {
57774
+ const safeStep = Number.isFinite(step) && step > 0 ? Math.floor(step) : 1;
57775
+ const options = [];
57776
+ for (let minute = 0; minute < 60; minute += safeStep) {
57777
+ options.push({ value: minute, label: pad(minute) });
57778
+ }
57779
+ return options;
57780
+ }
57781
+ /** The hour as the 12-hour column shows it (12, 1…11). */
57782
+ function toHour12(hour) {
57783
+ return hour % 12 === 0 ? 12 : hour % 12;
57784
+ }
57785
+ /** Rebuilds a 24-hour hour from a 12-hour column value and its period. */
57786
+ function fromHour12(hour12, period) {
57787
+ const base = hour12 % 12;
57788
+ return period === 'PM' ? base + 12 : base;
57789
+ }
57790
+ function periodOf(hour) {
57791
+ return hour < 12 ? 'AM' : 'PM';
57792
+ }
57793
+
57794
+ // Only one TimePicker popover should be open at a time — same coordination the
57795
+ // DateInput uses, for the same reason: each is its own `modal` Radix Popover, so
57796
+ // opening a second does not dismiss the first by outside-click.
57797
+ const TIMEPICKER_OPEN_EVENT = 'caratlogic:timepicker-open';
57798
+ const PERIOD_OPTIONS = [
57799
+ { value: 0, label: 'AM' },
57800
+ { value: 1, label: 'PM' },
57801
+ ];
57802
+ function TimePicker({ value, onChange, onBlur, name, className, disabled, placeholder = 'Select time', use24Hour = false, minuteStep = 1, hideFooter = false, }) {
57803
+ const [open, setOpen] = useState(false);
57804
+ const instanceId = useId$2();
57805
+ const parsed = parseTimeValue(value);
57806
+ useEffect(() => {
57807
+ const onOtherOpen = (e) => {
57808
+ if (e.detail !== instanceId)
57809
+ setOpen(false);
57810
+ };
57811
+ window.addEventListener(TIMEPICKER_OPEN_EVENT, onOtherOpen);
57812
+ return () => window.removeEventListener(TIMEPICKER_OPEN_EVENT, onOtherOpen);
57813
+ }, [instanceId]);
57814
+ const handleOpenChange = (next) => {
57815
+ setOpen(next);
57816
+ if (next) {
57817
+ window.dispatchEvent(new CustomEvent(TIMEPICKER_OPEN_EVENT, { detail: instanceId }));
57818
+ }
57819
+ };
57820
+ /** Picking one column before the others commits against a 00:00 baseline. */
57821
+ const commit = (hour, minute) => {
57822
+ onChange(toTimeValue(hour, minute));
57823
+ };
57824
+ const handleHour = (picked) => {
57825
+ const minute = parsed?.minute ?? 0;
57826
+ if (use24Hour) {
57827
+ commit(picked, minute);
57828
+ return;
57829
+ }
57830
+ commit(fromHour12(picked, parsed ? periodOf(parsed.hour) : 'AM'), minute);
57831
+ };
57832
+ const handleMinute = (picked) => {
57833
+ commit(parsed?.hour ?? 0, picked);
57834
+ };
57835
+ const handlePeriod = (picked) => {
57836
+ const period = picked === 0 ? 'AM' : 'PM';
57837
+ const hour12 = parsed ? toHour12(parsed.hour) : 12;
57838
+ commit(fromHour12(hour12, period), parsed?.minute ?? 0);
57839
+ };
57840
+ const selectedHour = parsed
57841
+ ? use24Hour
57842
+ ? parsed.hour
57843
+ : toHour12(parsed.hour)
57844
+ : null;
57845
+ // NOTE: `open` is driven from the button's onClick rather than
57846
+ // <PopoverTrigger>, matching DateInput — inside a modal <SlideOut> the
57847
+ // trigger's built-in toggle does not fire in Safari. <PopoverAnchor> is pure
57848
+ // positioning, so a single controlled toggle stays the only source of truth.
57849
+ return (jsxs(Popover, { open: open, onOpenChange: handleOpenChange, modal: true, children: [jsx(PopoverAnchor, { asChild: true, children: jsxs("button", { type: "button", name: name, onBlur: onBlur, disabled: disabled, "aria-haspopup": "dialog", "aria-expanded": open, onClick: () => {
57850
+ if (!disabled)
57851
+ handleOpenChange(!open);
57852
+ }, "data-slot": "input", "data-empty": !parsed || undefined, className: cn('border-input-border flex h-9 w-full min-w-0 cursor-pointer items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-1 text-left shadow-[0_1px_2px_0_#0000000d] transition-[color,box-shadow] outline-none', 'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]', 'aria-invalid:ring-destructive/20 aria-invalid:border-destructive', disabled && 'cursor-not-allowed bg-muted/50 text-label', className), children: [jsx("span", { className: cn('truncate text-sm md:text-sm', parsed ? 'text-foreground' : 'text-placeholder'), children: parsed ? formatTimeDisplay(parsed, use24Hour) : placeholder }), jsx("span", { className: "inline-flex h-4 w-4 shrink-0 items-center justify-center text-muted-foreground", children: jsx(Clock, { className: "h-4 w-4 stroke-[1.75]" }) })] }) }), jsxs(PopoverContent, { align: "start", className: "w-auto p-3", children: [jsxs("div", { className: "flex gap-1", children: [jsx(TimeColumn, { label: "Hour", options: buildHourOptions(use24Hour), selected: selectedHour, onSelect: handleHour }), jsx(TimeColumn, { label: "Min", options: buildMinuteOptions(minuteStep), selected: parsed?.minute ?? null, onSelect: handleMinute }), !use24Hour && (jsx(TimeColumn, { label: "AM/PM", options: PERIOD_OPTIONS, selected: parsed ? (periodOf(parsed.hour) === 'AM' ? 0 : 1) : null, onSelect: handlePeriod }))] }), !hideFooter && (jsxs("div", { className: "mt-2 flex items-center justify-between gap-2 border-t border-border pt-2", children: [jsx("button", { type: "button", onClick: () => {
57853
+ onChange(undefined);
57854
+ setOpen(false);
57855
+ }, className: "rounded-md px-2 py-1 text-xs font-medium text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: "Clear" }), jsx("button", { type: "button", onClick: () => {
57856
+ const now = new Date();
57857
+ commit(now.getHours(), now.getMinutes());
57858
+ setOpen(false);
57859
+ }, className: "rounded-md px-2 py-1 text-xs font-medium text-primary transition-colors hover:text-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: "Now" })] }))] })] }));
57860
+ }
57861
+
57669
57862
  function mapToItem(w, separator) {
57670
57863
  const id = w.id || w.warehouseId || '';
57671
57864
  if (!id)
@@ -67660,5 +67853,5 @@ var RichTextAreaImpl = /*#__PURE__*/Object.freeze({
67660
67853
  RichTextarea: RichTextarea
67661
67854
  });
67662
67855
 
67663
- export { ActionBadge, ActionButtonWithTooltip, AdvancedSearchDrawer, Alert, ApiComboBox, AppSidebar, AuditEntryCard, AuditHistoryPanel, AuditTimeline, AuditTimelineShimmer, Badge, BadgeType, BusinessContactStatusFilter, Button, CUSTOMER_AUDIT_CONFIG, CertificateRowList, ChangePill, Collapsible, CollapsibleCard, CollapsibleContent, CollapsibleTrigger, ConfirmDialog, ContactTypeFilter, CopyButton, CreateFormPageHeader, CreateFormPageShell, CurrencyDisplayTabs, CurrencyInput, CurrencyToggle, CustomCombobox, CustomTable, CustomizeColumnsDrawer, DIAMOND_AUDIT_CONFIG, DashboardShell, DataEditableTable, DataTable, DateInput, DetailChip, DetailPageLayout, DetailPageSkeleton, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EntityHeader, Field, FieldSkeleton, FileUploader, FilterMultiSelect, FilterSelect, FormSectionCard, FormSectionCardSkeleton, GEM_AUDIT_CONFIG, HistoryEntryCard, HistoryTimeline, HistoryTimelineShimmer, INVOICE_AUDIT_CONFIG, IconButton, IdHyperlinkText, InfoBanner, InventoryComboBox, InventoryItemCard, InventoryItemRow, InvoiceComboBox, KYCStatusFilter, KeyValueGrid, LAYOUT_AUDIT_CONFIG, LabeledToggle, MEMO_IN_AUDIT_CONFIG, MEMO_OUT_AUDIT_CONFIG, MediaGallery, MemoInComboBox, MetricCard, MetricRow, MetricTile, Modal, MoreInfoPanel, MoreInfoTrigger, Navbar, NavbarActions, NavbarAvatar, NavbarIconButton, NavbarSearch, NavbarTitle, NumberInput, NumberedStepCard, OtpInput, OwnershipFilter, OwnershipSplitCard, PROFORMA_AUDIT_CONFIG, PageHeader, PageTabs, Pagination, PaginationSelect, PartnerShareCard, PriceInput, Protected, PurchaseOrderComboBox, QUOTATION_AUDIT_CONFIG, RadioCardGroup, ReadMoreText, RefreshButton, RichTextarea$1 as RichTextarea, SEARCHABLE_SELECT_PAGE_SIZE, SafeImage, SearchBox, SearchInput, SearchableSelect, SectionCard, SectionIcon, SegmentedControl, ShareDialog, ShareMenu, ShimmerTable, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, SlideOut, SlideOutClose, SlideOutContent, SlideOutDescription, SlideOutHeader, SlideOutOverlay, SlideOutPortal, SlideOutTitle, SlideOutTrigger, Slider, SpecList, Spinner, SquareSkeleton, StatusBadge$1 as StatusBadge, StatusFilter, Stepper, StockTypeFilter, TablePagination, TableSkeleton, Tabs, TabsContent, TabsList, TabsTrigger, TagsInput, TextHyperlink, ToggleSwitch, TruncateTooltip, WarehouseComboBox, WarehouseFilter, ZoomSplitViewer, badgeVariants, buildBulkShareTitle, buildInventoryPublicUrl, buildInventoryRowProps, buildInventoryShareTitle, buildWhatsappUrl, buttonVariants, cn, createVideoMediaItem, dropdownMenuItemDestructiveClass, formatFileSize, generateActionDescription, generateTitle, getBadgeTypeForStatus, getChangePills, getInitials, iconButtonVariants, isPathActive, notify, releaseStaleOverlayBodyLock, renderBadge, useAuditShowMore, useBadge, useButton, useDropdownMenu, useNavbar, useSidebar, videoThumbnailSrc };
67856
+ export { ActionBadge, ActionButtonWithTooltip, AdvancedSearchDrawer, Alert, ApiComboBox, AppSidebar, AuditEntryCard, AuditHistoryPanel, AuditTimeline, AuditTimelineShimmer, Badge, BadgeType, BusinessContactStatusFilter, Button, CUSTOMER_AUDIT_CONFIG, CertificateRowList, ChangePill, Collapsible, CollapsibleCard, CollapsibleContent, CollapsibleTrigger, ConfirmDialog, ContactTypeFilter, CopyButton, CreateFormPageHeader, CreateFormPageShell, CurrencyDisplayTabs, CurrencyInput, CurrencyToggle, CustomCombobox, CustomTable, CustomizeColumnsDrawer, DIAMOND_AUDIT_CONFIG, DashboardShell, DataEditableTable, DataTable, DateInput, DetailChip, DetailPageLayout, DetailPageSkeleton, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EntityHeader, Field, FieldSkeleton, FileUploader, FilterMultiSelect, FilterSelect, FormSectionCard, FormSectionCardSkeleton, GEM_AUDIT_CONFIG, HistoryEntryCard, HistoryTimeline, HistoryTimelineShimmer, INVOICE_AUDIT_CONFIG, IconButton, IdHyperlinkText, InfoBanner, InventoryComboBox, InventoryItemCard, InventoryItemRow, InvoiceComboBox, KYCStatusFilter, KeyValueGrid, LAYOUT_AUDIT_CONFIG, LabeledToggle, MEMO_IN_AUDIT_CONFIG, MEMO_OUT_AUDIT_CONFIG, MediaGallery, MemoInComboBox, MetricCard, MetricRow, MetricTile, Modal, MoreInfoPanel, MoreInfoTrigger, Navbar, NavbarActions, NavbarAvatar, NavbarIconButton, NavbarSearch, NavbarTitle, NumberInput, NumberedStepCard, OtpInput, OwnershipFilter, OwnershipSplitCard, PROFORMA_AUDIT_CONFIG, PageHeader, PageTabs, Pagination, PaginationSelect, PartnerShareCard, PriceInput, Protected, PurchaseOrderComboBox, QUOTATION_AUDIT_CONFIG, RadioCardGroup, ReadMoreText, RefreshButton, RichTextarea$1 as RichTextarea, SEARCHABLE_SELECT_PAGE_SIZE, SafeImage, SearchBox, SearchInput, SearchableSelect, SectionCard, SectionIcon, SegmentedControl, ShareDialog, ShareMenu, ShimmerTable, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, SlideOut, SlideOutClose, SlideOutContent, SlideOutDescription, SlideOutHeader, SlideOutOverlay, SlideOutPortal, SlideOutTitle, SlideOutTrigger, Slider, SpecList, Spinner, SquareSkeleton, StatusBadge$1 as StatusBadge, StatusFilter, Stepper, StockTypeFilter, TablePagination, TableSkeleton, Tabs, TabsContent, TabsList, TabsTrigger, TagsInput, TextHyperlink, TimePicker, ToggleSwitch, TruncateTooltip, WarehouseComboBox, WarehouseFilter, ZoomSplitViewer, badgeVariants, buildBulkShareTitle, buildInventoryPublicUrl, buildInventoryRowProps, buildInventoryShareTitle, buildWhatsappUrl, buttonVariants, cn, createVideoMediaItem, dropdownMenuItemDestructiveClass, formatFileSize, generateActionDescription, generateTitle, getBadgeTypeForStatus, getChangePills, getInitials, iconButtonVariants, isPathActive, notify, releaseStaleOverlayBodyLock, renderBadge, useAuditShowMore, useBadge, useButton, useDropdownMenu, useNavbar, useSidebar, videoThumbnailSrc, watchOverlayBodyLock };
67664
67857
  //# sourceMappingURL=index.esm.js.map