@papernote/ui 1.10.14 → 1.10.16

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.esm.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
2
  import * as React from 'react';
3
3
  import React__default, { forwardRef, useState, useRef, useEffect, useCallback, useId, useImperativeHandle, useMemo, Children, isValidElement, cloneElement, Component, createContext as createContext$1, useContext, useLayoutEffect, createElement, useReducer } from 'react';
4
- import { Loader2, Check, X, EyeOff, Eye, AlertTriangle, CheckCircle, AlertCircle, ChevronDown, Search, Minus, Star, Calendar as Calendar$1, ChevronLeft, ChevronRight, Clock, ChevronUp, Plus, TrendingUp, TrendingDown, Lightbulb, Sparkles, PartyPopper, Flame, Shield, Pencil, User, Users, Activity, Mail, Send, Info, Trash2, ChevronsLeft, ChevronsRight, Circle, MoreVertical, GripVertical, Upload, Bold, Italic, Underline, List, ListOrdered, Code, Link, MoreHorizontal, Home, FileText, Image, File as File$1, Menu as Menu$1, ArrowDown, Settings, LogOut, Moon, Sun, Bell, ExternalLink, Edit, Trash, Pin, PinOff, Download, Save, ArrowUpDown, Filter, XCircle, BarChart3, MessageSquare } from 'lucide-react';
4
+ import { Loader2, Check, X, EyeOff, Eye, AlertTriangle, CheckCircle, AlertCircle, ChevronDown, Search, Minus, Star, Calendar as Calendar$1, ChevronLeft, ChevronRight, Clock, ChevronUp, Plus, TrendingUp, TrendingDown, Lightbulb, Sparkles, PartyPopper, Flame, Shield, Pencil, User, Users, Activity, Mail, Send, Info, Trash2, HelpCircle, ChevronsLeft, ChevronsRight, Circle, MoreVertical, GripVertical, Upload, Bold, Italic, Underline, List, ListOrdered, Code, Link, ExternalLink, MoreHorizontal, Home, FileText, Image, File as File$1, Menu as Menu$1, ArrowDown, Settings, LogOut, Moon, Sun, Bell, Edit, Trash, Pin, PinOff, Download, Save, ArrowUpDown, Filter, XCircle, BarChart3, MessageSquare } from 'lucide-react';
5
5
  import { createPortal } from 'react-dom';
6
- import { useInRouterContext, useNavigate, useLocation, Link as Link$1 } from 'react-router-dom';
6
+ import { Link as Link$1, useInRouterContext, useNavigate, useLocation } from 'react-router-dom';
7
7
 
8
8
  function _mergeNamespaces(n, m) {
9
9
  m.forEach(function (e) {
@@ -702,7 +702,7 @@ function usePrefersMobile() {
702
702
  }
703
703
 
704
704
  // Size classes for trigger button
705
- const sizeClasses$a = {
705
+ const sizeClasses$b = {
706
706
  sm: 'h-8 text-sm py-1',
707
707
  md: 'h-10 text-base py-2',
708
708
  lg: 'h-12 text-base py-3 min-h-touch', // 44px touch target
@@ -1019,14 +1019,14 @@ const Select = forwardRef((props, ref) => {
1019
1019
  if (useNativeSelect) {
1020
1020
  return (jsxs("div", { className: "w-full", children: [label && (jsx("label", { id: labelId, className: "label", children: label })), jsxs("div", { className: "relative", children: [jsxs("select", { ref: nativeSelectRef, value: value || '', onChange: (e) => onChange?.(e.target.value), disabled: disabled, className: `
1021
1021
  input w-full appearance-none pr-10
1022
- ${sizeClasses$a[effectiveSize]}
1022
+ ${sizeClasses$b[effectiveSize]}
1023
1023
  ${error ? 'border-error-400 focus:border-error-400 focus:ring-error-400' : ''}
1024
1024
  ${disabled ? 'opacity-40 cursor-not-allowed' : 'cursor-pointer'}
1025
1025
  `, "aria-labelledby": label ? labelId : undefined, "aria-invalid": error ? 'true' : undefined, "aria-describedby": error ? errorId : (helperText ? helperTextId : undefined), children: [jsx("option", { value: "", disabled: true, children: placeholder }), options.map((opt) => (jsx("option", { value: opt.value, disabled: opt.disabled, children: opt.label }, opt.value))), groups.map((group) => (jsx("optgroup", { label: group.label, children: group.options.map((opt) => (jsx("option", { value: opt.value, disabled: opt.disabled, children: opt.label }, opt.value))) }, group.label)))] }), jsx(ChevronDown, { className: "absolute right-3 top-1/2 -translate-y-1/2 h-5 w-5 text-ink-500 pointer-events-none" })] }), error && (jsx("p", { id: errorId, className: "mt-2 text-xs text-error-600", role: "alert", "aria-live": "assertive", children: error })), helperText && !error && (jsx("p", { id: helperTextId, className: "mt-2 text-xs text-ink-600", children: helperText }))] }));
1026
1026
  }
1027
1027
  return (jsxs("div", { className: "w-full", children: [label && (jsx("label", { id: labelId, className: "label", children: label })), jsx("div", { ref: selectRef, className: "relative", children: jsxs("button", { ref: buttonRef, type: "button", onClick: () => !disabled && setIsOpen(!isOpen), disabled: disabled, className: `
1028
1028
  input w-full flex items-center justify-between px-3
1029
- ${sizeClasses$a[effectiveSize]}
1029
+ ${sizeClasses$b[effectiveSize]}
1030
1030
  ${error ? 'border-error-400 focus:border-error-400 focus:ring-error-400' : ''}
1031
1031
  ${disabled ? 'opacity-40 cursor-not-allowed' : 'cursor-pointer'}
1032
1032
  `, role: "combobox", "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-controls": listboxId, "aria-labelledby": label ? labelId : undefined, "aria-label": !label ? placeholder : undefined, "aria-activedescendant": activeDescendant, "aria-invalid": error ? 'true' : undefined, "aria-describedby": error ? errorId : (helperText ? helperTextId : undefined), "aria-disabled": disabled, children: [jsxs("span", { className: `flex items-center gap-2 ${selectedOption ? 'text-ink-800' : 'text-ink-400'}`, children: [loading && jsx(Loader2, { className: "h-4 w-4 animate-spin text-ink-500" }), !loading && selectedOption?.icon && jsx("span", { children: selectedOption.icon }), selectedOption ? selectedOption.label : placeholder] }), jsxs("div", { className: "flex items-center gap-1", children: [clearable && value && (jsx("button", { type: "button", onClick: (e) => {
@@ -1171,7 +1171,7 @@ const Switch = forwardRef(({ checked, onChange, label, description, disabled = f
1171
1171
  Switch.displayName = 'Switch';
1172
1172
 
1173
1173
  // Size classes for textarea
1174
- const sizeClasses$9 = {
1174
+ const sizeClasses$a = {
1175
1175
  sm: 'px-3 py-2 text-sm',
1176
1176
  md: 'px-4 py-3 text-sm',
1177
1177
  lg: 'px-4 py-3.5 text-base', // Larger padding and text for mobile
@@ -1259,7 +1259,7 @@ const Textarea = forwardRef(({ label, helperText, validationState, validationMes
1259
1259
  bg-white bg-subtle-grain transition-all duration-200
1260
1260
  focus:outline-none focus:ring-2 ${getResizeClass()}
1261
1261
  disabled:bg-paper-100 disabled:text-ink-400 disabled:cursor-not-allowed disabled:opacity-60
1262
- ${sizeClasses$9[effectiveSize]}
1262
+ ${sizeClasses$a[effectiveSize]}
1263
1263
  ${getValidationClasses()}
1264
1264
  ${loading ? 'pr-10' : ''}
1265
1265
  ${className}
@@ -4586,7 +4586,7 @@ function BottomSheetActions({ children, className = '' }) {
4586
4586
  return (jsx("div", { className: `flex flex-col gap-2 px-4 py-4 border-t border-paper-200 bg-white ${className}`, children: children }));
4587
4587
  }
4588
4588
 
4589
- const sizeClasses$8 = {
4589
+ const sizeClasses$9 = {
4590
4590
  sm: 'max-w-md',
4591
4591
  md: 'max-w-lg',
4592
4592
  lg: 'max-w-2xl',
@@ -4726,7 +4726,7 @@ function Modal({ isOpen, onClose, title, children, size = 'md', showCloseButton
4726
4726
  return createPortal(jsx(BottomSheet, { isOpen: isOpen, onClose: onClose, title: title, height: mobileHeight, showHandle: mobileShowHandle, showCloseButton: showCloseButton, children: children }), document.body);
4727
4727
  }
4728
4728
  // Render as standard modal on desktop
4729
- const modalContent = (jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center p-4 bg-ink-900 bg-opacity-50 backdrop-blur-sm animate-fade-in", onMouseDown: handleBackdropMouseDown, onClick: handleBackdropClick, children: jsxs("div", { ref: modalRef, className: `${sizeClasses$8[size]} w-full bg-white bg-subtle-grain rounded-xl shadow-2xl border border-paper-200 ${getAnimationClass()}`, role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, children: [jsxs("div", { className: "flex items-center justify-between px-6 py-4 border-b border-paper-200", children: [jsx("h3", { id: titleId, className: "text-lg font-medium text-ink-900", children: title }), showCloseButton && (jsx("button", { onClick: onClose, className: "text-ink-400 hover:text-ink-600 transition-colors", "aria-label": "Close modal", children: jsx(X, { className: "h-5 w-5" }) }))] }), jsx("div", { className: `px-6 py-4 ${scrollable || maxHeight ? 'overflow-y-auto' : ''}`, style: {
4729
+ const modalContent = (jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center p-4 bg-ink-900 bg-opacity-50 backdrop-blur-sm animate-fade-in", onMouseDown: handleBackdropMouseDown, onClick: handleBackdropClick, children: jsxs("div", { ref: modalRef, className: `${sizeClasses$9[size]} w-full bg-white bg-subtle-grain rounded-xl shadow-2xl border border-paper-200 ${getAnimationClass()}`, role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, children: [jsxs("div", { className: "flex items-center justify-between px-6 py-4 border-b border-paper-200", children: [jsx("h3", { id: titleId, className: "text-lg font-medium text-ink-900", children: title }), showCloseButton && (jsx("button", { onClick: onClose, className: "text-ink-400 hover:text-ink-600 transition-colors", "aria-label": "Close modal", children: jsx(X, { className: "h-5 w-5" }) }))] }), jsx("div", { className: `px-6 py-4 ${scrollable || maxHeight ? 'overflow-y-auto' : ''}`, style: {
4730
4730
  maxHeight: maxHeight || (scrollable ? 'calc(100vh - 200px)' : undefined),
4731
4731
  }, children: children })] }) }));
4732
4732
  return createPortal(modalContent, document.body);
@@ -5789,7 +5789,7 @@ function Alert({ variant = 'info', title, children, onClose, className = '', act
5789
5789
  return (jsx("div", { className: `rounded-lg border p-4 ${styles.container} ${className}`, role: "alert", children: jsxs("div", { className: "flex items-start gap-3", children: [jsx("div", { className: "flex-shrink-0 mt-0.5", children: styles.icon }), jsxs("div", { className: "flex-1 min-w-0", children: [title && jsx("h4", { className: "text-sm font-medium mb-1", children: title }), jsx("div", { className: "text-sm", children: children }), actions.length > 0 && (jsx("div", { className: "flex gap-2 mt-3", children: actions.map((action, index) => (jsx("button", { onClick: action.onClick, className: getButtonStyles(action.variant), children: action.label }, index))) }))] }), onClose && (jsx("button", { onClick: onClose, className: "flex-shrink-0 text-current opacity-70 hover:opacity-100 transition-opacity", "aria-label": "Close alert", children: jsx(X, { className: "h-4 w-4" }) }))] }) }));
5790
5790
  }
5791
5791
 
5792
- const sizeClasses$7 = {
5792
+ const sizeClasses$8 = {
5793
5793
  left: {
5794
5794
  sm: 'w-64',
5795
5795
  md: 'w-96',
@@ -5868,7 +5868,7 @@ function Drawer({ isOpen, onClose, title, children, placement = 'right', size =
5868
5868
  const isHorizontal = placement === 'left' || placement === 'right';
5869
5869
  return (jsxs("div", { className: "fixed inset-0 z-50 flex", children: [showOverlay && (jsx("div", { className: "fixed inset-0 bg-ink-900 bg-opacity-50 backdrop-blur-sm animate-fade-in", onClick: handleOverlayClick, "aria-hidden": "true" })), jsxs("div", { className: `
5870
5870
  fixed ${placementClasses[placement]}
5871
- ${sizeClasses$7[placement][size]}
5871
+ ${sizeClasses$8[placement][size]}
5872
5872
  bg-white border-paper-200 shadow-2xl
5873
5873
  ${isHorizontal ? 'border-r' : 'border-b'}
5874
5874
  ${animationClasses[placement].enter}
@@ -6010,6 +6010,16 @@ function useConfirmDialog() {
6010
6010
  };
6011
6011
  }
6012
6012
 
6013
+ const sizeClasses$7 = {
6014
+ sm: 'h-3.5 w-3.5',
6015
+ md: 'h-4 w-4',
6016
+ lg: 'h-5 w-5',
6017
+ };
6018
+ function HelpTooltip({ content, icon = 'help', size = 'md', position = 'top', className = '', }) {
6019
+ const IconComponent = icon === 'info' ? Info : HelpCircle;
6020
+ return (jsx(Tooltip, { content: content, position: position, children: jsx("span", { className: `inline-flex items-center justify-center text-ink-400 hover:text-ink-600 cursor-help transition-colors ${className}`, role: "button", "aria-label": "Help", tabIndex: 0, children: jsx(IconComponent, { className: sizeClasses$7[size] }) }) }));
6021
+ }
6022
+
6013
6023
  function Popover({ trigger: triggerElement, children, placement = 'bottom', triggerMode = 'click', showArrow = true, offset = 8, open: controlledOpen, onOpenChange, closeOnClickOutside = true, closeOnEscape = true, showDelay = 0, hideDelay = 0, className = '', disabled = false, }) {
6014
6024
  const [internalOpen, setInternalOpen] = useState(false);
6015
6025
  const [position, setPosition] = useState({ top: 0, left: 0 });
@@ -9350,6 +9360,370 @@ function Collapsible({ trigger, children, defaultOpen = false, open: controlledO
9350
9360
  ` }))] }), jsx("div", { id: "collapsible-content", ref: contentRef, className: "overflow-hidden transition-all duration-300 ease-in-out", style: { height: `${height}px` }, "aria-hidden": !isOpen, children: jsx("div", { className: contentClassName, children: children }) })] }));
9351
9361
  }
9352
9362
 
9363
+ function Badge({ children, variant = 'neutral', size = 'md', icon, onRemove, className = '', dot = false, pill = false, truncate = false, maxWidth, animate = false, }) {
9364
+ const variantStyles = {
9365
+ success: 'bg-success-50 text-success-700 border-success-200',
9366
+ warning: 'bg-warning-50 text-warning-700 border-warning-200',
9367
+ error: 'bg-error-50 text-error-700 border-error-200',
9368
+ info: 'bg-primary-50 text-primary-700 border-primary-200',
9369
+ neutral: 'bg-paper-100 text-ink-700 border-paper-300',
9370
+ };
9371
+ const dotVariantStyles = {
9372
+ success: 'bg-success-500',
9373
+ warning: 'bg-warning-500',
9374
+ error: 'bg-error-500',
9375
+ info: 'bg-primary-500',
9376
+ neutral: 'bg-ink-400',
9377
+ };
9378
+ const sizeStyles = {
9379
+ sm: 'px-2 py-0.5 text-xs gap-1',
9380
+ md: 'px-3 py-1 text-xs gap-1.5',
9381
+ lg: 'px-3 py-1.5 text-sm gap-2',
9382
+ };
9383
+ // Pill variant has tighter horizontal padding and fully rounded ends
9384
+ const pillSizeStyles = {
9385
+ sm: 'px-1.5 py-0.5 text-xs gap-1',
9386
+ md: 'px-2 py-0.5 text-xs gap-1',
9387
+ lg: 'px-2.5 py-1 text-sm gap-1.5',
9388
+ };
9389
+ const dotSizeStyles = {
9390
+ sm: 'h-1.5 w-1.5',
9391
+ md: 'h-2 w-2',
9392
+ lg: 'h-2.5 w-2.5',
9393
+ };
9394
+ const iconSize = {
9395
+ sm: 'h-3 w-3',
9396
+ md: 'h-3.5 w-3.5',
9397
+ lg: 'h-4 w-4',
9398
+ };
9399
+ // Dot variant - just a colored circle
9400
+ if (dot) {
9401
+ return (jsx("span", { className: `
9402
+ inline-block rounded-full
9403
+ ${dotVariantStyles[variant]}
9404
+ ${dotSizeStyles[size]}
9405
+ ${animate ? 'animate-fade-in' : ''}
9406
+ ${className}
9407
+ `, "aria-label": `${variant} indicator` }));
9408
+ }
9409
+ // Regular badge
9410
+ return (jsxs("span", { className: `
9411
+ inline-flex items-center border font-medium whitespace-nowrap
9412
+ ${pill ? 'rounded-full' : 'rounded-full'}
9413
+ ${variantStyles[variant]}
9414
+ ${pill ? pillSizeStyles[size] : sizeStyles[size]}
9415
+ ${truncate ? 'max-w-full overflow-hidden' : ''}
9416
+ ${animate ? 'animate-fade-in' : ''}
9417
+ ${className}
9418
+ `, style: maxWidth ? { maxWidth } : undefined, children: [icon && jsx("span", { className: `${iconSize[size]} flex-shrink-0`, children: icon }), jsx("span", { className: truncate ? 'truncate' : '', children: children }), onRemove && (jsx("button", { onClick: onRemove, className: "ml-1 hover:opacity-70 transition-opacity flex-shrink-0", "aria-label": "Remove badge", children: jsx(X, { className: iconSize[size] }) }))] }));
9419
+ }
9420
+
9421
+ /**
9422
+ * CollapsibleSection - A card-style collapsible container for dashboard sections
9423
+ *
9424
+ * Wraps content with a styled header that includes title, optional icon, badge count,
9425
+ * and "View All" navigation. Supports both controlled and uncontrolled modes for
9426
+ * localStorage persistence via onOpenChange callback.
9427
+ *
9428
+ * @example Basic usage
9429
+ * ```tsx
9430
+ * <CollapsibleSection
9431
+ * title="Upcoming Bills"
9432
+ * badge={5}
9433
+ * badgeVariant="warning"
9434
+ * viewAllHref="/bills"
9435
+ * >
9436
+ * <BillsList bills={upcomingBills} />
9437
+ * </CollapsibleSection>
9438
+ * ```
9439
+ *
9440
+ * @example With localStorage persistence
9441
+ * ```tsx
9442
+ * const [isOpen, setIsOpen] = useState(() =>
9443
+ * localStorage.getItem('section-open') !== 'false'
9444
+ * );
9445
+ *
9446
+ * <CollapsibleSection
9447
+ * title="Pending Items"
9448
+ * badge={pendingCount}
9449
+ * open={isOpen}
9450
+ * onOpenChange={(open) => {
9451
+ * setIsOpen(open);
9452
+ * localStorage.setItem('section-open', String(open));
9453
+ * }}
9454
+ * >
9455
+ * {children}
9456
+ * </CollapsibleSection>
9457
+ * ```
9458
+ */
9459
+ function CollapsibleSection({ title, icon, badge, badgeVariant = 'neutral', viewAllHref, viewAllLabel = 'View All', onViewAll, defaultOpen = true, open: controlledOpen, onOpenChange, children, className = '', }) {
9460
+ const isControlled = controlledOpen !== undefined;
9461
+ const [internalOpen, setInternalOpen] = useState(defaultOpen);
9462
+ const isOpen = isControlled ? controlledOpen : internalOpen;
9463
+ const contentRef = useRef(null);
9464
+ const [height, setHeight] = useState(0);
9465
+ const handleToggle = () => {
9466
+ const newOpen = !isOpen;
9467
+ if (!isControlled) {
9468
+ setInternalOpen(newOpen);
9469
+ }
9470
+ onOpenChange?.(newOpen);
9471
+ };
9472
+ const handleViewAllClick = (e) => {
9473
+ if (onViewAll) {
9474
+ e.preventDefault();
9475
+ onViewAll();
9476
+ }
9477
+ };
9478
+ // Update height when content changes or open state changes
9479
+ useEffect(() => {
9480
+ if (!contentRef.current)
9481
+ return;
9482
+ if (isOpen) {
9483
+ const contentHeight = contentRef.current.scrollHeight;
9484
+ setHeight(contentHeight);
9485
+ }
9486
+ else {
9487
+ setHeight(0);
9488
+ }
9489
+ }, [isOpen, children]);
9490
+ // Recalculate height when window resizes
9491
+ useEffect(() => {
9492
+ if (!isOpen)
9493
+ return;
9494
+ const handleResize = () => {
9495
+ if (contentRef.current) {
9496
+ setHeight(contentRef.current.scrollHeight);
9497
+ }
9498
+ };
9499
+ window.addEventListener('resize', handleResize);
9500
+ return () => window.removeEventListener('resize', handleResize);
9501
+ }, [isOpen]);
9502
+ const ViewAllContent = (jsxs(Fragment, { children: [jsx("span", { children: viewAllLabel }), jsx(ExternalLink, { className: "h-3.5 w-3.5" })] }));
9503
+ return (jsxs("div", { className: `
9504
+ bg-white bg-subtle-grain border-2 border-paper-300 rounded-xl shadow-sm
9505
+ ${className}
9506
+ `, children: [jsxs("div", { className: "flex items-center justify-between px-5 py-4 border-b border-paper-200", children: [jsxs("button", { type: "button", onClick: handleToggle, className: "flex items-center gap-3 text-left flex-1 min-w-0 group", "aria-expanded": isOpen, children: [jsx(ChevronDown, { className: `
9507
+ h-5 w-5 text-ink-400 transition-transform duration-200 flex-shrink-0
9508
+ group-hover:text-ink-600
9509
+ ${isOpen ? 'rotate-0' : '-rotate-90'}
9510
+ ` }), icon && (jsx("span", { className: "flex-shrink-0 text-ink-500", children: icon })), jsx("span", { className: "font-medium text-ink-900 truncate", children: title }), badge !== undefined && (jsx(Badge, { variant: badgeVariant, size: "sm", pill: true, children: badge }))] }), (viewAllHref || onViewAll) && (viewAllHref ? (jsx(Link$1, { to: viewAllHref, onClick: onViewAll ? handleViewAllClick : undefined, className: "flex items-center gap-1.5 text-sm text-primary-600 hover:text-primary-700 font-medium flex-shrink-0 ml-4", children: ViewAllContent })) : (jsx("button", { onClick: onViewAll, className: "flex items-center gap-1.5 text-sm text-primary-600 hover:text-primary-700 font-medium flex-shrink-0 ml-4", children: ViewAllContent })))] }), jsx("div", { ref: contentRef, className: "overflow-hidden transition-all duration-300 ease-in-out", style: { height: `${height}px` }, "aria-hidden": !isOpen, children: jsx("div", { className: "p-5", children: children }) })] }));
9511
+ }
9512
+
9513
+ /**
9514
+ * SummaryCard - Dashboard metric card with trend indicators and actions
9515
+ *
9516
+ * Displays a key metric with optional trend indicator, subtitle, and quick actions.
9517
+ * Suitable for dashboard grids showing financial summaries, goal progress, etc.
9518
+ *
9519
+ * @example Basic metric
9520
+ * ```tsx
9521
+ * <SummaryCard
9522
+ * title="Net Worth"
9523
+ * icon={<Wallet className="h-5 w-5" />}
9524
+ * value={125000}
9525
+ * valuePrefix="$"
9526
+ * trend={{ direction: 'up', value: '12.5%', label: 'vs last month' }}
9527
+ * />
9528
+ * ```
9529
+ *
9530
+ * @example With actions
9531
+ * ```tsx
9532
+ * <SummaryCard
9533
+ * title="Monthly Income"
9534
+ * value="$8,500"
9535
+ * trend={{ direction: 'up', value: '+$500' }}
9536
+ * subtitle="3 income sources"
9537
+ * actions={[
9538
+ * { label: 'View Details', href: '/income' },
9539
+ * { label: 'Add Income', onClick: openAddModal }
9540
+ * ]}
9541
+ * />
9542
+ * ```
9543
+ *
9544
+ * @example Clickable card
9545
+ * ```tsx
9546
+ * <SummaryCard
9547
+ * title="Goals Progress"
9548
+ * value="3/5"
9549
+ * valueSuffix=" completed"
9550
+ * href="/goals"
9551
+ * subtitle="2 goals due this month"
9552
+ * />
9553
+ * ```
9554
+ */
9555
+ function SummaryCard({ title, icon, value, valuePrefix, valueSuffix, trend, subtitle, href, onClick, actions = [], className = '', loading = false, }) {
9556
+ const isClickable = !!(href || onClick);
9557
+ const handleCardClick = () => {
9558
+ if (onClick) {
9559
+ onClick();
9560
+ }
9561
+ };
9562
+ const handleKeyDown = (e) => {
9563
+ if (isClickable && (e.key === 'Enter' || e.key === ' ')) {
9564
+ e.preventDefault();
9565
+ handleCardClick();
9566
+ }
9567
+ };
9568
+ const getTrendColor = (direction) => {
9569
+ switch (direction) {
9570
+ case 'up':
9571
+ return 'text-success-600';
9572
+ case 'down':
9573
+ return 'text-error-600';
9574
+ case 'neutral':
9575
+ return 'text-ink-500';
9576
+ }
9577
+ };
9578
+ const getTrendIcon = (direction) => {
9579
+ switch (direction) {
9580
+ case 'up':
9581
+ return jsx(TrendingUp, { className: "h-4 w-4" });
9582
+ case 'down':
9583
+ return jsx(TrendingDown, { className: "h-4 w-4" });
9584
+ case 'neutral':
9585
+ return jsx(Minus, { className: "h-4 w-4" });
9586
+ }
9587
+ };
9588
+ const cardContent = (jsxs(Fragment, { children: [jsxs("div", { className: "flex items-center gap-2 mb-3", children: [icon && (jsx("span", { className: "flex-shrink-0 text-ink-500", children: icon })), jsx("span", { className: "text-sm font-medium text-ink-600 truncate", children: title }), isClickable && (jsx(ChevronRight, { className: "h-4 w-4 text-ink-400 ml-auto flex-shrink-0" }))] }), jsxs("div", { className: "text-3xl font-semibold text-ink-900 tracking-tight", children: [valuePrefix, typeof value === 'number' ? value.toLocaleString() : value, valueSuffix && (jsx("span", { className: "text-lg font-normal text-ink-500", children: valueSuffix }))] }), trend && (jsxs("div", { className: `flex items-center gap-1.5 mt-2 text-sm font-medium ${getTrendColor(trend.direction)}`, children: [getTrendIcon(trend.direction), jsx("span", { children: trend.value }), trend.label && (jsx("span", { className: "text-ink-500 font-normal", children: trend.label }))] })), subtitle && (jsx("div", { className: "text-sm text-ink-500 mt-2", children: subtitle })), actions.length > 0 && (jsx("div", { className: "flex flex-wrap gap-3 mt-4 pt-3 border-t border-paper-200", children: actions.map((action, index) => {
9589
+ const actionContent = (jsx("span", { className: "text-sm font-medium text-primary-600 hover:text-primary-700", children: action.label }));
9590
+ return action.href ? (jsx(Link$1, { to: action.href, onClick: (e) => e.stopPropagation(), children: actionContent }, index)) : (jsx("button", { onClick: (e) => {
9591
+ e.stopPropagation();
9592
+ action.onClick?.();
9593
+ }, className: "text-sm font-medium text-primary-600 hover:text-primary-700", children: action.label }, index));
9594
+ }) }))] }));
9595
+ const cardProps = {
9596
+ variant: 'compact',
9597
+ loading,
9598
+ className: `${className} ${isClickable ? 'cursor-pointer' : ''}`,
9599
+ hoverable: isClickable,
9600
+ ...(onClick && !href && {
9601
+ onClick: handleCardClick,
9602
+ role: 'button',
9603
+ tabIndex: 0,
9604
+ onKeyDown: handleKeyDown,
9605
+ }),
9606
+ };
9607
+ // If href is provided, wrap in Link
9608
+ if (href) {
9609
+ return (jsx(Link$1, { to: href, className: "block", children: jsx(Card, { ...cardProps, children: cardContent }) }));
9610
+ }
9611
+ return (jsx(Card, { ...cardProps, children: cardContent }));
9612
+ }
9613
+
9614
+ /**
9615
+ * PriorityAlertBanner - Displays urgent/action-required alerts
9616
+ *
9617
+ * Shows a stack of priority alerts at the top of a page. Supports multiple
9618
+ * severity levels, dismissible alerts, and navigation to related pages.
9619
+ * Auto-hides when no alerts are present.
9620
+ *
9621
+ * @example Basic usage
9622
+ * ```tsx
9623
+ * <PriorityAlertBanner
9624
+ * alerts={[
9625
+ * { id: '1', severity: 'error', message: 'overdue bills', count: 3, href: '/bills' },
9626
+ * { id: '2', severity: 'warning', message: 'Bank sync failed', dismissible: true }
9627
+ * ]}
9628
+ * onDismiss={(id) => dismissAlert(id)}
9629
+ * />
9630
+ * ```
9631
+ *
9632
+ * @example Sticky at top of page
9633
+ * ```tsx
9634
+ * <PriorityAlertBanner
9635
+ * alerts={alerts}
9636
+ * onDismiss={handleDismiss}
9637
+ * sticky
9638
+ * />
9639
+ * ```
9640
+ */
9641
+ function PriorityAlertBanner({ alerts, onDismiss, sticky = false, className = '', }) {
9642
+ // Don't render if no alerts
9643
+ if (alerts.length === 0) {
9644
+ return null;
9645
+ }
9646
+ const getSeverityStyles = (severity) => {
9647
+ switch (severity) {
9648
+ case 'error':
9649
+ return {
9650
+ container: 'bg-error-50 border-error-200 text-error-900',
9651
+ icon: jsx(AlertCircle, { className: "h-5 w-5 text-error-600 flex-shrink-0" }),
9652
+ hoverBg: 'hover:bg-error-100',
9653
+ dismissHover: 'hover:text-error-700 hover:bg-error-100',
9654
+ };
9655
+ case 'warning':
9656
+ return {
9657
+ container: 'bg-warning-50 border-warning-200 text-warning-900',
9658
+ icon: jsx(AlertTriangle, { className: "h-5 w-5 text-warning-600 flex-shrink-0" }),
9659
+ hoverBg: 'hover:bg-warning-100',
9660
+ dismissHover: 'hover:text-warning-700 hover:bg-warning-100',
9661
+ };
9662
+ case 'info':
9663
+ return {
9664
+ container: 'bg-primary-50 border-primary-200 text-primary-900',
9665
+ icon: jsx(Info, { className: "h-5 w-5 text-primary-600 flex-shrink-0" }),
9666
+ hoverBg: 'hover:bg-primary-100',
9667
+ dismissHover: 'hover:text-primary-700 hover:bg-primary-100',
9668
+ };
9669
+ }
9670
+ };
9671
+ const formatAlertContent = (alert) => {
9672
+ const parts = [];
9673
+ if (alert.count !== undefined) {
9674
+ parts.push(alert.count.toString());
9675
+ }
9676
+ if (alert.amount !== undefined) {
9677
+ parts.push(new Intl.NumberFormat('en-US', {
9678
+ style: 'currency',
9679
+ currency: 'USD',
9680
+ }).format(alert.amount));
9681
+ }
9682
+ if (parts.length > 0) {
9683
+ return `${parts.join(' • ')} ${alert.message}`;
9684
+ }
9685
+ return alert.message;
9686
+ };
9687
+ const renderAlert = (alert) => {
9688
+ const styles = getSeverityStyles(alert.severity);
9689
+ const isClickable = !!(alert.href || alert.onClick);
9690
+ const content = formatAlertContent(alert);
9691
+ const alertContent = (jsxs("div", { className: "flex items-center gap-3 flex-1 min-w-0", children: [styles.icon, jsx("span", { className: "text-sm font-medium truncate", children: content }), isClickable && (jsx(ChevronRight, { className: "h-4 w-4 opacity-60 flex-shrink-0" }))] }));
9692
+ const handleClick = (e) => {
9693
+ if (alert.onClick) {
9694
+ e.preventDefault();
9695
+ alert.onClick();
9696
+ }
9697
+ };
9698
+ const handleDismiss = (e) => {
9699
+ e.preventDefault();
9700
+ e.stopPropagation();
9701
+ onDismiss?.(alert.id);
9702
+ };
9703
+ const containerClassName = `
9704
+ flex items-center gap-2 px-4 py-3 border-b last:border-b-0
9705
+ ${styles.container}
9706
+ ${isClickable ? `cursor-pointer ${styles.hoverBg} transition-colors` : ''}
9707
+ `;
9708
+ const dismissButton = alert.dismissible && onDismiss && (jsx("button", { onClick: handleDismiss, className: `
9709
+ p-1 rounded-md opacity-70 hover:opacity-100 transition-all
9710
+ ${styles.dismissHover}
9711
+ `, "aria-label": "Dismiss alert", children: jsx(X, { className: "h-4 w-4" }) }));
9712
+ if (alert.href) {
9713
+ return (jsxs(Link$1, { to: alert.href, onClick: alert.onClick ? handleClick : undefined, className: containerClassName, children: [alertContent, dismissButton] }, alert.id));
9714
+ }
9715
+ if (alert.onClick) {
9716
+ return (jsxs("button", { onClick: handleClick, className: `${containerClassName} w-full text-left`, children: [alertContent, dismissButton] }, alert.id));
9717
+ }
9718
+ return (jsxs("div", { className: containerClassName, children: [alertContent, dismissButton] }, alert.id));
9719
+ };
9720
+ return (jsx("div", { className: `
9721
+ bg-white border border-paper-300 rounded-lg shadow-sm overflow-hidden
9722
+ ${sticky ? 'sticky top-0 z-50' : ''}
9723
+ ${className}
9724
+ `, role: "alert", "aria-live": "polite", children: alerts.map(renderAlert) }));
9725
+ }
9726
+
9353
9727
  const sizeClasses$2 = {
9354
9728
  sm: {
9355
9729
  header: 'h-10 px-3',
@@ -10881,64 +11255,6 @@ function Breadcrumbs({ items, showHome = true }) {
10881
11255
  })] }));
10882
11256
  }
10883
11257
 
10884
- function Badge({ children, variant = 'neutral', size = 'md', icon, onRemove, className = '', dot = false, pill = false, truncate = false, maxWidth, animate = false, }) {
10885
- const variantStyles = {
10886
- success: 'bg-success-50 text-success-700 border-success-200',
10887
- warning: 'bg-warning-50 text-warning-700 border-warning-200',
10888
- error: 'bg-error-50 text-error-700 border-error-200',
10889
- info: 'bg-primary-50 text-primary-700 border-primary-200',
10890
- neutral: 'bg-paper-100 text-ink-700 border-paper-300',
10891
- };
10892
- const dotVariantStyles = {
10893
- success: 'bg-success-500',
10894
- warning: 'bg-warning-500',
10895
- error: 'bg-error-500',
10896
- info: 'bg-primary-500',
10897
- neutral: 'bg-ink-400',
10898
- };
10899
- const sizeStyles = {
10900
- sm: 'px-2 py-0.5 text-xs gap-1',
10901
- md: 'px-3 py-1 text-xs gap-1.5',
10902
- lg: 'px-3 py-1.5 text-sm gap-2',
10903
- };
10904
- // Pill variant has tighter horizontal padding and fully rounded ends
10905
- const pillSizeStyles = {
10906
- sm: 'px-1.5 py-0.5 text-xs gap-1',
10907
- md: 'px-2 py-0.5 text-xs gap-1',
10908
- lg: 'px-2.5 py-1 text-sm gap-1.5',
10909
- };
10910
- const dotSizeStyles = {
10911
- sm: 'h-1.5 w-1.5',
10912
- md: 'h-2 w-2',
10913
- lg: 'h-2.5 w-2.5',
10914
- };
10915
- const iconSize = {
10916
- sm: 'h-3 w-3',
10917
- md: 'h-3.5 w-3.5',
10918
- lg: 'h-4 w-4',
10919
- };
10920
- // Dot variant - just a colored circle
10921
- if (dot) {
10922
- return (jsx("span", { className: `
10923
- inline-block rounded-full
10924
- ${dotVariantStyles[variant]}
10925
- ${dotSizeStyles[size]}
10926
- ${animate ? 'animate-fade-in' : ''}
10927
- ${className}
10928
- `, "aria-label": `${variant} indicator` }));
10929
- }
10930
- // Regular badge
10931
- return (jsxs("span", { className: `
10932
- inline-flex items-center border font-medium whitespace-nowrap
10933
- ${pill ? 'rounded-full' : 'rounded-full'}
10934
- ${variantStyles[variant]}
10935
- ${pill ? pillSizeStyles[size] : sizeStyles[size]}
10936
- ${truncate ? 'max-w-full overflow-hidden' : ''}
10937
- ${animate ? 'animate-fade-in' : ''}
10938
- ${className}
10939
- `, style: maxWidth ? { maxWidth } : undefined, children: [icon && jsx("span", { className: `${iconSize[size]} flex-shrink-0`, children: icon }), jsx("span", { className: truncate ? 'truncate' : '', children: children }), onRemove && (jsx("button", { onClick: onRemove, className: "ml-1 hover:opacity-70 transition-opacity flex-shrink-0", "aria-label": "Remove badge", children: jsx(X, { className: iconSize[size] }) }))] }));
10940
- }
10941
-
10942
11258
  const TabsContext = createContext$1(null);
10943
11259
  function useTabsContext() {
10944
11260
  const context = useContext(TabsContext);
@@ -60992,5 +61308,5 @@ function Responsive({ mobile, tablet, desktop, }) {
60992
61308
  return jsx(Fragment, { children: mobile || tablet || desktop });
60993
61309
  }
60994
61310
 
60995
- export { Accordion, AchievementBadge, AchievementUnlock, ActionBar, ActionBarCenter, ActionBarLeft, ActionBarRight, ActionButton, ActivityFeed, AdminModal, Alert, AlertDialog, AppLayout, Autocomplete, Avatar, BREAKPOINTS, Badge, BottomNavigation, BottomNavigationSpacer, BottomSheet, BottomSheetActions, BottomSheetContent, BottomSheetHeader, Box, Breadcrumbs, Button, ButtonGroup, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CardView, Carousel, Celebration, Checkbox, CheckboxList, Chip, ChipGroup, CollaboratorAvatars, Collapsible, ColorPicker, Combobox, ComingSoon, CommandPalette, CompactStat, ConfirmDialog, ContextMenu, ControlBar, CurrencyDisplay, CurrencyInput, Dashboard, DashboardContent, DashboardHeader, DataGrid, DataTable, DataTableCardView, DateDisplay, DatePicker, DateRangePicker, DateTimePicker, DesktopOnly, Drawer, DrawerFooter, DropZone, Dropdown, DropdownTrigger, EmptyState, ErrorBoundary, ExpandablePanel, ExpandablePanelContainer, ExpandablePanelSpacer, ExpandableRowButton, ExpandableToolbar, ExpandedRowEditForm, ExportButton, FORMULA_CATEGORIES, FORMULA_DEFINITIONS, FORMULA_NAMES, FieldArray, FileUpload, FilterBar, FilterControls, FilterStatusBanner, FloatingActionButton, Form, FormContext, FormControl, FormWizard, Grid, GridItem, Hide, HorizontalScroll, HoverCard, InfiniteScroll, Input, InviteCard, KanbanBoard, Layout, Loading, LoadingOverlay, Logo, MarkdownEditor, MaskedInput, Menu, MenuDivider, MobileHeader, MobileHeaderSpacer, MobileLayout, MobileOnly, MobileProvider, Modal, ModalFooter, MotivationalMessage, MultiSelect, NotificationBanner, NotificationBar, NotificationBell, NotificationIndicator, NumberInput, Page, PageHeader, PageLayout, PageNavigation, Pagination, PasswordInput, PermissionBadge, Popover, Progress, ProgressCelebration, PullToRefresh, QueryTransparency, RadioGroup, Rating, Responsive, RichTextEditor, SearchBar, SearchableList, Select, Separator, SharedBadge, Show, Sidebar, SidebarGroup, Skeleton, SkeletonCard$1 as SkeletonCard, SkeletonTable, Slider, Spreadsheet, SpreadsheetReport, Stack, StatCard, StatItem, StatsCardGrid, StatsGrid, StatusBadge, StatusBar, StepIndicator, Stepper, StreakBadge, SuccessCheck, SwipeActions, SwipeableCard, SwipeableListItem, Switch, Tabs, TabsContent, TabsList, TabsRoot, TabsTrigger, Text, Textarea, ThemeToggle, TimePicker, Timeline, TimezoneSelector, Toast, ToastContainer, Tooltip, Transfer, TreeView, TwoColumnContent, UserProfileButton, addErrorMessage, addInfoMessage, addSuccessMessage, addWarningMessage, calculateColumnWidth, createActionsSection, createFiltersSection, createMultiSheetExcel, createPageControlsSection, createQueryDetailsSection, exportDataTableToExcel, exportToExcel, formatStatisticValue, formatStatistics, getFormula, getFormulasByCategory, getLocalTimezone, isValidTimezone, loadColumnOrder, loadColumnWidths, reorderArray, saveColumnOrder, saveColumnWidths, searchFormulas, statusManager, useBreadcrumbReset, useBreakpoint, useBreakpointValue, useCelebration, useColumnReorder, useColumnResize, useCommandPalette, useConfirmDialog, useDelighters, useFABScroll, useFormContext, useIsDesktop, useIsMobile, useIsTablet, useIsTouchDevice, useMediaQuery, useMobileContext, useOrientation, usePrefersMobile, useResponsiveCallback, useSafeAreaInsets, useViewportSize, withMobileContext };
61311
+ export { Accordion, AchievementBadge, AchievementUnlock, ActionBar, ActionBarCenter, ActionBarLeft, ActionBarRight, ActionButton, ActivityFeed, AdminModal, Alert, AlertDialog, AppLayout, Autocomplete, Avatar, BREAKPOINTS, Badge, BottomNavigation, BottomNavigationSpacer, BottomSheet, BottomSheetActions, BottomSheetContent, BottomSheetHeader, Box, Breadcrumbs, Button, ButtonGroup, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CardView, Carousel, Celebration, Checkbox, CheckboxList, Chip, ChipGroup, CollaboratorAvatars, Collapsible, CollapsibleSection, ColorPicker, Combobox, ComingSoon, CommandPalette, CompactStat, ConfirmDialog, ContextMenu, ControlBar, CurrencyDisplay, CurrencyInput, Dashboard, DashboardContent, DashboardHeader, DataGrid, DataTable, DataTableCardView, DateDisplay, DatePicker, DateRangePicker, DateTimePicker, DesktopOnly, Drawer, DrawerFooter, DropZone, Dropdown, DropdownTrigger, EmptyState, ErrorBoundary, ExpandablePanel, ExpandablePanelContainer, ExpandablePanelSpacer, ExpandableRowButton, ExpandableToolbar, ExpandedRowEditForm, ExportButton, FORMULA_CATEGORIES, FORMULA_DEFINITIONS, FORMULA_NAMES, FieldArray, FileUpload, FilterBar, FilterControls, FilterStatusBanner, FloatingActionButton, Form, FormContext, FormControl, FormWizard, Grid, GridItem, HelpTooltip, Hide, HorizontalScroll, HoverCard, InfiniteScroll, Input, InviteCard, KanbanBoard, Layout, Loading, LoadingOverlay, Logo, MarkdownEditor, MaskedInput, Menu, MenuDivider, MobileHeader, MobileHeaderSpacer, MobileLayout, MobileOnly, MobileProvider, Modal, ModalFooter, MotivationalMessage, MultiSelect, NotificationBanner, NotificationBar, NotificationBell, NotificationIndicator, NumberInput, Page, PageHeader, PageLayout, PageNavigation, Pagination, PasswordInput, PermissionBadge, Popover, PriorityAlertBanner, Progress, ProgressCelebration, PullToRefresh, QueryTransparency, RadioGroup, Rating, Responsive, RichTextEditor, SearchBar, SearchableList, Select, Separator, SharedBadge, Show, Sidebar, SidebarGroup, Skeleton, SkeletonCard$1 as SkeletonCard, SkeletonTable, Slider, Spreadsheet, SpreadsheetReport, Stack, StatCard, StatItem, StatsCardGrid, StatsGrid, StatusBadge, StatusBar, StepIndicator, Stepper, StreakBadge, SuccessCheck, SummaryCard, SwipeActions, SwipeableCard, SwipeableListItem, Switch, Tabs, TabsContent, TabsList, TabsRoot, TabsTrigger, Text, Textarea, ThemeToggle, TimePicker, Timeline, TimezoneSelector, Toast, ToastContainer, Tooltip, Transfer, TreeView, TwoColumnContent, UserProfileButton, addErrorMessage, addInfoMessage, addSuccessMessage, addWarningMessage, calculateColumnWidth, createActionsSection, createFiltersSection, createMultiSheetExcel, createPageControlsSection, createQueryDetailsSection, exportDataTableToExcel, exportToExcel, formatStatisticValue, formatStatistics, getFormula, getFormulasByCategory, getLocalTimezone, isValidTimezone, loadColumnOrder, loadColumnWidths, reorderArray, saveColumnOrder, saveColumnWidths, searchFormulas, statusManager, useBreadcrumbReset, useBreakpoint, useBreakpointValue, useCelebration, useColumnReorder, useColumnResize, useCommandPalette, useConfirmDialog, useDelighters, useFABScroll, useFormContext, useIsDesktop, useIsMobile, useIsTablet, useIsTouchDevice, useMediaQuery, useMobileContext, useOrientation, usePrefersMobile, useResponsiveCallback, useSafeAreaInsets, useViewportSize, withMobileContext };
60996
61312
  //# sourceMappingURL=index.esm.js.map