@papernote/ui 1.7.7 → 1.8.0

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.
Files changed (42) hide show
  1. package/dist/components/Badge.d.ts +3 -1
  2. package/dist/components/Badge.d.ts.map +1 -1
  3. package/dist/components/BottomSheet.d.ts +72 -8
  4. package/dist/components/BottomSheet.d.ts.map +1 -1
  5. package/dist/components/CompactStat.d.ts +52 -0
  6. package/dist/components/CompactStat.d.ts.map +1 -0
  7. package/dist/components/HorizontalScroll.d.ts +43 -0
  8. package/dist/components/HorizontalScroll.d.ts.map +1 -0
  9. package/dist/components/NotificationBanner.d.ts +53 -0
  10. package/dist/components/NotificationBanner.d.ts.map +1 -0
  11. package/dist/components/Progress.d.ts +2 -2
  12. package/dist/components/Progress.d.ts.map +1 -1
  13. package/dist/components/PullToRefresh.d.ts +23 -71
  14. package/dist/components/PullToRefresh.d.ts.map +1 -1
  15. package/dist/components/Stack.d.ts +2 -1
  16. package/dist/components/Stack.d.ts.map +1 -1
  17. package/dist/components/SwipeableCard.d.ts +65 -0
  18. package/dist/components/SwipeableCard.d.ts.map +1 -0
  19. package/dist/components/Text.d.ts +9 -2
  20. package/dist/components/Text.d.ts.map +1 -1
  21. package/dist/components/index.d.ts +11 -3
  22. package/dist/components/index.d.ts.map +1 -1
  23. package/dist/index.d.ts +317 -86
  24. package/dist/index.esm.js +932 -253
  25. package/dist/index.esm.js.map +1 -1
  26. package/dist/index.js +937 -252
  27. package/dist/index.js.map +1 -1
  28. package/dist/styles.css +178 -8
  29. package/package.json +1 -1
  30. package/src/components/Badge.tsx +13 -2
  31. package/src/components/BottomSheet.tsx +227 -98
  32. package/src/components/Card.tsx +1 -1
  33. package/src/components/CompactStat.tsx +150 -0
  34. package/src/components/HorizontalScroll.tsx +275 -0
  35. package/src/components/NotificationBanner.tsx +238 -0
  36. package/src/components/Progress.tsx +6 -3
  37. package/src/components/PullToRefresh.tsx +158 -196
  38. package/src/components/Stack.tsx +4 -1
  39. package/src/components/SwipeableCard.tsx +347 -0
  40. package/src/components/Text.tsx +45 -3
  41. package/src/components/index.ts +16 -3
  42. package/src/styles/index.css +32 -0
package/dist/index.esm.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
2
  import * as React from 'react';
3
3
  import React__default, { forwardRef, useState, useEffect, useCallback, useRef, useId, useImperativeHandle, useMemo, Children, isValidElement, cloneElement, Component, createContext as createContext$1, useLayoutEffect, createElement, useContext, useReducer } from 'react';
4
- import { Loader2, X, EyeOff, Eye, AlertTriangle, CheckCircle, AlertCircle, ChevronDown, Search, Check, Minus, Star, Calendar as Calendar$1, ChevronLeft, ChevronRight, Clock, ChevronUp, Plus, TrendingUp, TrendingDown, Info, Trash2, ChevronsLeft, ChevronsRight, Circle, MoreVertical, GripVertical, Upload, Bold, Italic, Underline, List, ListOrdered, Code, Link, Home, FileText, Image, File as File$1, Menu as Menu$1, ArrowDown, User, Settings, LogOut, Moon, Sun, Bell, Edit, Trash, Pin, PinOff, Download, Save, ArrowUpDown, Filter, XCircle, BarChart3, MessageSquare } from 'lucide-react';
4
+ import { Loader2, X, EyeOff, Eye, AlertTriangle, CheckCircle, AlertCircle, ChevronDown, Search, Check, Minus, Star, Calendar as Calendar$1, ChevronLeft, ChevronRight, Clock, ChevronUp, Plus, TrendingUp, TrendingDown, 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, User, 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
6
  import { useInRouterContext, useNavigate, useLocation, Link as Link$1 } from 'react-router-dom';
7
7
 
@@ -2507,7 +2507,7 @@ const Card = forwardRef(({ children, variant = 'default', width = 'auto', classN
2507
2507
  const baseStyles = 'bg-white bg-subtle-grain border-2 border-paper-300 transition-shadow duration-200';
2508
2508
  const variantStyles = {
2509
2509
  default: 'rounded-xl shadow-lg p-8',
2510
- compact: 'rounded-lg shadow-md p-5',
2510
+ compact: 'rounded-lg shadow-md p-3', // 12px padding for mobile-density layouts
2511
2511
  flat: 'rounded-lg p-5',
2512
2512
  };
2513
2513
  const widthStyles = {
@@ -2623,6 +2623,7 @@ function Separator({ orientation = 'horizontal', className = '', spacing = 'md',
2623
2623
  *
2624
2624
  * Spacing scale (use either `spacing` or `gap` prop - they're aliases):
2625
2625
  * - none: 0
2626
+ * - tight: 0.25rem (1) - for mobile-density layouts
2626
2627
  * - xs: 0.5rem (2)
2627
2628
  * - sm: 0.75rem (3)
2628
2629
  * - md: 1.5rem (6)
@@ -2650,6 +2651,7 @@ const Stack = forwardRef(({ children, direction = 'vertical', spacing, gap, alig
2650
2651
  const spacingClasses = {
2651
2652
  vertical: {
2652
2653
  none: '',
2654
+ tight: 'space-y-1', // 4px - for mobile-density layouts
2653
2655
  xs: 'space-y-2',
2654
2656
  sm: 'space-y-3',
2655
2657
  md: 'space-y-6',
@@ -2658,6 +2660,7 @@ const Stack = forwardRef(({ children, direction = 'vertical', spacing, gap, alig
2658
2660
  },
2659
2661
  horizontal: {
2660
2662
  none: '',
2663
+ tight: 'space-x-1', // 4px - for mobile-density layouts
2661
2664
  xs: 'space-x-2',
2662
2665
  sm: 'space-x-3',
2663
2666
  md: 'space-x-6',
@@ -2955,7 +2958,7 @@ const GridItem = ({ colSpan, rowSpan, children, className = '', ...boxProps }) =
2955
2958
  * <Text ref={textRef}>Measurable text</Text>
2956
2959
  * ```
2957
2960
  */
2958
- const Text = forwardRef(({ children, as: Component = 'p', size = 'base', weight = 'normal', color = 'primary', align = 'left', truncate = false, lineClamp, transform, className = '', ...htmlProps }, ref) => {
2961
+ const Text = forwardRef(({ children, as: Component = 'p', size = 'base', smSize, mdSize, lgSize, weight = 'normal', color = 'primary', align = 'left', truncate = false, lineClamp, transform, className = '', ...htmlProps }, ref) => {
2959
2962
  const sizeClasses = {
2960
2963
  xs: 'text-xs',
2961
2964
  sm: 'text-sm',
@@ -2964,6 +2967,31 @@ const Text = forwardRef(({ children, as: Component = 'p', size = 'base', weight
2964
2967
  xl: 'text-xl',
2965
2968
  '2xl': 'text-2xl',
2966
2969
  };
2970
+ // Responsive size classes
2971
+ const smSizeClasses = {
2972
+ xs: 'sm:text-xs',
2973
+ sm: 'sm:text-sm',
2974
+ base: 'sm:text-base',
2975
+ lg: 'sm:text-lg',
2976
+ xl: 'sm:text-xl',
2977
+ '2xl': 'sm:text-2xl',
2978
+ };
2979
+ const mdSizeClasses = {
2980
+ xs: 'md:text-xs',
2981
+ sm: 'md:text-sm',
2982
+ base: 'md:text-base',
2983
+ lg: 'md:text-lg',
2984
+ xl: 'md:text-xl',
2985
+ '2xl': 'md:text-2xl',
2986
+ };
2987
+ const lgSizeClasses = {
2988
+ xs: 'lg:text-xs',
2989
+ sm: 'lg:text-sm',
2990
+ base: 'lg:text-base',
2991
+ lg: 'lg:text-lg',
2992
+ xl: 'lg:text-xl',
2993
+ '2xl': 'lg:text-2xl',
2994
+ };
2967
2995
  const weightClasses = {
2968
2996
  normal: 'font-normal',
2969
2997
  medium: 'font-medium',
@@ -3001,6 +3029,9 @@ const Text = forwardRef(({ children, as: Component = 'p', size = 'base', weight
3001
3029
  // Build class list
3002
3030
  const classes = [
3003
3031
  sizeClasses[size],
3032
+ smSize ? smSizeClasses[smSize] : '',
3033
+ mdSize ? mdSizeClasses[mdSize] : '',
3034
+ lgSize ? lgSizeClasses[lgSize] : '',
3004
3035
  weightClasses[weight],
3005
3036
  colorClasses[color],
3006
3037
  alignClasses[align],
@@ -3107,24 +3138,48 @@ function Alert({ variant = 'info', title, children, onClose, className = '', act
3107
3138
  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" }) }))] }) }));
3108
3139
  }
3109
3140
 
3110
- const heightPresets = {
3111
- sm: '33vh',
3112
- md: '50vh',
3113
- lg: '75vh',
3114
- full: '90vh',
3115
- };
3116
- function BottomSheet({ isOpen, onClose, children, title, height = 'md', showHandle = true, showCloseButton = true, closeOnOverlayClick = true, closeOnEscape = true, className = '', }) {
3117
- const titleId = useId();
3141
+ /**
3142
+ * BottomSheet - Mobile-friendly modal that slides up from the bottom
3143
+ *
3144
+ * Designed for mobile contexts with touch-friendly interactions:
3145
+ * - Drag handle for swipe-to-dismiss
3146
+ * - Snap points for partial expansion
3147
+ * - Sticky action area at thumb zone
3148
+ *
3149
+ * @example
3150
+ * ```tsx
3151
+ * <BottomSheet open={isOpen} onClose={() => setIsOpen(false)}>
3152
+ * <BottomSheetHeader>
3153
+ * <Text weight="bold">Transaction Details</Text>
3154
+ * </BottomSheetHeader>
3155
+ * <BottomSheetContent>
3156
+ * {content}
3157
+ * </BottomSheetContent>
3158
+ * <BottomSheetActions>
3159
+ * <Button fullWidth>Approve</Button>
3160
+ * </BottomSheetActions>
3161
+ * </BottomSheet>
3162
+ * ```
3163
+ */
3164
+ function BottomSheet({ open, isOpen, onClose, children, title, height = 'auto', maxHeight = '90vh', snapPoints, closeOnOverlayClick = true, closeOnEscape = true, showHandle = true, showCloseButton = true, preventScroll = true, className = '', }) {
3165
+ // Support both 'open' and 'isOpen' props for flexibility
3166
+ const isSheetOpen = open ?? isOpen ?? false;
3167
+ // Height presets for convenience
3168
+ const heightPresets = {
3169
+ sm: '40vh',
3170
+ md: '60vh',
3171
+ lg: '80vh',
3172
+ full: '100vh',
3173
+ };
3174
+ const sheetRef = useRef(null);
3118
3175
  const [isDragging, setIsDragging] = useState(false);
3119
3176
  const [dragOffset, setDragOffset] = useState(0);
3120
- const [currentHeight] = useState(typeof height === 'string' && height in heightPresets
3121
- ? heightPresets[height]
3122
- : height);
3123
- const sheetRef = useRef(null);
3124
- const startYRef = useRef(0);
3125
- // Close on Escape
3177
+ const [currentSnapIndex, setCurrentSnapIndex] = useState(snapPoints?.length ? snapPoints.length - 1 : 0);
3178
+ const startY = useRef(0);
3179
+ const startOffset = useRef(0);
3180
+ // Handle escape key
3126
3181
  useEffect(() => {
3127
- if (!isOpen || !closeOnEscape)
3182
+ if (!isSheetOpen || !closeOnEscape)
3128
3183
  return;
3129
3184
  const handleEscape = (e) => {
3130
3185
  if (e.key === 'Escape') {
@@ -3133,77 +3188,132 @@ function BottomSheet({ isOpen, onClose, children, title, height = 'md', showHand
3133
3188
  };
3134
3189
  document.addEventListener('keydown', handleEscape);
3135
3190
  return () => document.removeEventListener('keydown', handleEscape);
3136
- }, [isOpen, closeOnEscape, onClose]);
3137
- // Prevent body scroll when open
3191
+ }, [open, closeOnEscape, onClose]);
3192
+ // Prevent body scroll
3138
3193
  useEffect(() => {
3139
- if (isOpen) {
3140
- document.body.style.overflow = 'hidden';
3141
- }
3142
- else {
3143
- document.body.style.overflow = '';
3144
- }
3194
+ if (!isSheetOpen || !preventScroll)
3195
+ return;
3196
+ const originalOverflow = document.body.style.overflow;
3197
+ document.body.style.overflow = 'hidden';
3145
3198
  return () => {
3146
- document.body.style.overflow = '';
3199
+ document.body.style.overflow = originalOverflow;
3147
3200
  };
3148
- }, [isOpen]);
3149
- const handleOverlayClick = (e) => {
3150
- if (closeOnOverlayClick && e.target === e.currentTarget) {
3151
- onClose();
3152
- }
3153
- };
3154
- const handleDragStart = (e) => {
3201
+ }, [open, preventScroll]);
3202
+ // Handle drag start
3203
+ const handleDragStart = useCallback((clientY) => {
3155
3204
  setIsDragging(true);
3156
- const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY;
3157
- startYRef.current = clientY;
3158
- };
3159
- const handleDragMove = (e) => {
3205
+ startY.current = clientY;
3206
+ startOffset.current = dragOffset;
3207
+ }, [dragOffset]);
3208
+ // Handle drag move
3209
+ const handleDragMove = useCallback((clientY) => {
3210
+ if (!isDragging)
3211
+ return;
3212
+ const delta = clientY - startY.current;
3213
+ const newOffset = Math.max(0, startOffset.current + delta);
3214
+ setDragOffset(newOffset);
3215
+ }, [isDragging]);
3216
+ // Handle drag end
3217
+ const handleDragEnd = useCallback(() => {
3160
3218
  if (!isDragging)
3161
3219
  return;
3162
- const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY;
3163
- const offset = clientY - startYRef.current;
3164
- // Only allow dragging down
3165
- if (offset > 0) {
3166
- setDragOffset(offset);
3167
- }
3168
- };
3169
- const handleDragEnd = () => {
3170
3220
  setIsDragging(false);
3171
- // Close if dragged down more than 150px
3172
- if (dragOffset > 150) {
3173
- onClose();
3221
+ const threshold = 100; // pixels to trigger close
3222
+ if (dragOffset > threshold) {
3223
+ // If we have snap points, snap to next lower point or close
3224
+ if (snapPoints && currentSnapIndex > 0) {
3225
+ setCurrentSnapIndex(currentSnapIndex - 1);
3226
+ setDragOffset(0);
3227
+ }
3228
+ else {
3229
+ onClose();
3230
+ setDragOffset(0);
3231
+ }
3232
+ }
3233
+ else {
3234
+ // Snap back
3235
+ setDragOffset(0);
3174
3236
  }
3175
- setDragOffset(0);
3237
+ }, [isDragging, dragOffset, snapPoints, currentSnapIndex, onClose]);
3238
+ // Touch event handlers
3239
+ const handleTouchStart = (e) => {
3240
+ handleDragStart(e.touches[0].clientY);
3241
+ };
3242
+ const handleTouchMove = (e) => {
3243
+ handleDragMove(e.touches[0].clientY);
3244
+ };
3245
+ const handleTouchEnd = () => {
3246
+ handleDragEnd();
3247
+ };
3248
+ // Mouse event handlers (for desktop testing)
3249
+ const handleMouseDown = (e) => {
3250
+ handleDragStart(e.clientY);
3176
3251
  };
3177
3252
  useEffect(() => {
3178
3253
  if (!isDragging)
3179
3254
  return;
3180
- const handleMove = (e) => handleDragMove(e);
3181
- const handleEnd = () => handleDragEnd();
3182
- document.addEventListener('touchmove', handleMove);
3183
- document.addEventListener('mousemove', handleMove);
3184
- document.addEventListener('touchend', handleEnd);
3185
- document.addEventListener('mouseup', handleEnd);
3255
+ const handleMouseMove = (e) => {
3256
+ handleDragMove(e.clientY);
3257
+ };
3258
+ const handleMouseUp = () => {
3259
+ handleDragEnd();
3260
+ };
3261
+ document.addEventListener('mousemove', handleMouseMove);
3262
+ document.addEventListener('mouseup', handleMouseUp);
3186
3263
  return () => {
3187
- document.removeEventListener('touchmove', handleMove);
3188
- document.removeEventListener('mousemove', handleMove);
3189
- document.removeEventListener('touchend', handleEnd);
3190
- document.removeEventListener('mouseup', handleEnd);
3264
+ document.removeEventListener('mousemove', handleMouseMove);
3265
+ document.removeEventListener('mouseup', handleMouseUp);
3191
3266
  };
3192
- }, [isDragging, dragOffset]);
3193
- if (!isOpen)
3267
+ }, [isDragging, handleDragMove, handleDragEnd]);
3268
+ // Calculate height based on snap points or presets
3269
+ const getSheetHeight = () => {
3270
+ if (snapPoints && snapPoints[currentSnapIndex]) {
3271
+ return snapPoints[currentSnapIndex];
3272
+ }
3273
+ if (typeof height === 'number') {
3274
+ return `${height}px`;
3275
+ }
3276
+ // Check for preset heights
3277
+ if (typeof height === 'string' && heightPresets[height]) {
3278
+ return heightPresets[height];
3279
+ }
3280
+ return height;
3281
+ };
3282
+ if (!isSheetOpen)
3194
3283
  return null;
3195
- return (jsxs("div", { className: "fixed inset-0 z-50 flex items-end", onClick: handleOverlayClick, children: [jsx("div", { className: `
3284
+ const sheetContent = (jsxs("div", { className: "fixed inset-0 z-50", children: [jsx("div", { className: `
3196
3285
  absolute inset-0 bg-black/50 transition-opacity duration-300
3197
- ${isOpen ? 'opacity-100' : 'opacity-0'}
3198
- ` }), jsxs("div", { ref: sheetRef, className: `
3199
- relative w-full bg-white rounded-t-2xl shadow-2xl
3286
+ ${isSheetOpen ? 'opacity-100' : 'opacity-0'}
3287
+ `, onClick: closeOnOverlayClick ? onClose : undefined, "aria-hidden": "true" }), jsxs("div", { ref: sheetRef, className: `
3288
+ absolute bottom-0 left-0 right-0
3289
+ bg-white rounded-t-2xl shadow-2xl
3200
3290
  transition-transform duration-300 ease-out
3201
- ${isOpen ? 'translate-y-0' : 'translate-y-full'}
3291
+ ${isDragging ? 'transition-none' : ''}
3202
3292
  ${className}
3203
3293
  `, style: {
3204
- height: currentHeight,
3294
+ height: getSheetHeight(),
3295
+ maxHeight,
3205
3296
  transform: `translateY(${dragOffset}px)`,
3206
- }, role: "dialog", "aria-modal": "true", "aria-labelledby": title ? titleId : undefined, children: [showHandle && (jsx("div", { className: "py-3 cursor-grab active:cursor-grabbing", onTouchStart: handleDragStart, onMouseDown: handleDragStart, children: jsx("div", { className: "w-12 h-1.5 bg-ink-300 rounded-full mx-auto" }) })), (title || showCloseButton) && (jsxs("div", { className: "px-6 py-4 border-b border-ink-200 flex items-center justify-between", children: [title && (jsx("h2", { id: titleId, className: "text-lg font-semibold text-ink-900", children: title })), showCloseButton && (jsx("button", { onClick: onClose, className: "text-ink-400 hover:text-ink-600 transition-colors ml-auto", "aria-label": "Close", children: jsx(X, { className: "h-5 w-5" }) }))] })), jsx("div", { className: "overflow-y-auto flex-1 p-6", children: children })] })] }));
3297
+ }, role: "dialog", "aria-modal": "true", children: [showHandle && (jsx("div", { className: "flex justify-center pt-3 pb-2 cursor-grab active:cursor-grabbing touch-none", onTouchStart: handleTouchStart, onTouchMove: handleTouchMove, onTouchEnd: handleTouchEnd, onMouseDown: handleMouseDown, children: jsx("div", { className: "w-10 h-1 bg-paper-300 rounded-full" }) })), title && (jsxs("div", { className: "flex items-center justify-between px-4 py-3 border-b border-paper-200", children: [jsx("h2", { className: "text-lg font-medium text-ink-900", children: title }), showCloseButton && (jsx("button", { onClick: onClose, className: "p-1 rounded-full text-ink-500 hover:text-ink-700 hover:bg-paper-100 transition-colors", "aria-label": "Close", children: jsx(X, { className: "h-5 w-5" }) }))] })), jsx("div", { className: "flex flex-col h-full overflow-hidden", children: children })] })] }));
3298
+ return createPortal(sheetContent, document.body);
3299
+ }
3300
+ /**
3301
+ * BottomSheetHeader - Header section with title and optional close button
3302
+ */
3303
+ function BottomSheetHeader({ children, className = '' }) {
3304
+ return (jsx("div", { className: `flex items-center justify-between px-4 py-3 border-b border-paper-200 ${className}`, children: children }));
3305
+ }
3306
+ /**
3307
+ * BottomSheetContent - Scrollable content area
3308
+ */
3309
+ function BottomSheetContent({ children, className = '' }) {
3310
+ return (jsx("div", { className: `flex-1 overflow-y-auto px-4 py-4 ${className}`, children: children }));
3311
+ }
3312
+ /**
3313
+ * BottomSheetActions - Sticky footer for action buttons (thumb zone)
3314
+ */
3315
+ function BottomSheetActions({ children, className = '' }) {
3316
+ return (jsx("div", { className: `flex flex-col gap-2 px-4 py-4 border-t border-paper-200 bg-white ${className}`, children: children }));
3207
3317
  }
3208
3318
 
3209
3319
  const sizeClasses$8 = {
@@ -7338,6 +7448,598 @@ function Hide({ children, above, below, only, className = '' }) {
7338
7448
  return (jsx("div", { className: `${visibilityClasses} ${className}`, children: children }));
7339
7449
  }
7340
7450
 
7451
+ /**
7452
+ * HorizontalScroll - Horizontally scrollable container with peek indicators
7453
+ *
7454
+ * Designed for mobile carousels of cards with:
7455
+ * - Touch-friendly momentum scrolling
7456
+ * - Peek hint showing more items exist
7457
+ * - Optional snap scrolling
7458
+ * - Navigation arrows for desktop
7459
+ *
7460
+ * @example
7461
+ * ```tsx
7462
+ * <HorizontalScroll gap="md" peekAmount={24} showIndicators>
7463
+ * <Card>Bill 1</Card>
7464
+ * <Card>Bill 2</Card>
7465
+ * <Card>Bill 3</Card>
7466
+ * </HorizontalScroll>
7467
+ * ```
7468
+ */
7469
+ function HorizontalScroll({ children, gap = 'md', peekAmount = 24, showIndicators = false, snapToItem = true, showArrows = 'hover', scrollBehavior = 'smooth', className = '', scrollClassName = '', }) {
7470
+ const scrollRef = useRef(null);
7471
+ const [canScrollLeft, setCanScrollLeft] = useState(false);
7472
+ const [canScrollRight, setCanScrollRight] = useState(false);
7473
+ const [activeIndex, setActiveIndex] = useState(0);
7474
+ const [itemCount, setItemCount] = useState(0);
7475
+ const [isHovered, setIsHovered] = useState(false);
7476
+ // Gap classes
7477
+ const gapClasses = {
7478
+ none: 'gap-0',
7479
+ sm: 'gap-2',
7480
+ md: 'gap-4',
7481
+ lg: 'gap-6',
7482
+ };
7483
+ const gapStyle = typeof gap === 'number' ? { gap: `${gap}px` } : {};
7484
+ const gapClass = typeof gap === 'string' ? gapClasses[gap] : '';
7485
+ // Check scroll position and update state
7486
+ const checkScrollPosition = useCallback(() => {
7487
+ const container = scrollRef.current;
7488
+ if (!container)
7489
+ return;
7490
+ const { scrollLeft, scrollWidth, clientWidth } = container;
7491
+ setCanScrollLeft(scrollLeft > 0);
7492
+ setCanScrollRight(scrollLeft < scrollWidth - clientWidth - 1);
7493
+ // Calculate active index based on scroll position
7494
+ if (showIndicators && container.children.length > 0) {
7495
+ const children = Array.from(container.children);
7496
+ const containerRect = container.getBoundingClientRect();
7497
+ const containerCenter = containerRect.left + containerRect.width / 2;
7498
+ let closestIndex = 0;
7499
+ let closestDistance = Infinity;
7500
+ children.forEach((child, index) => {
7501
+ const childRect = child.getBoundingClientRect();
7502
+ const childCenter = childRect.left + childRect.width / 2;
7503
+ const distance = Math.abs(childCenter - containerCenter);
7504
+ if (distance < closestDistance) {
7505
+ closestDistance = distance;
7506
+ closestIndex = index;
7507
+ }
7508
+ });
7509
+ setActiveIndex(closestIndex);
7510
+ }
7511
+ }, [showIndicators]);
7512
+ // Initialize and handle resize
7513
+ useEffect(() => {
7514
+ const container = scrollRef.current;
7515
+ if (!container)
7516
+ return;
7517
+ setItemCount(React__default.Children.count(children));
7518
+ checkScrollPosition();
7519
+ const resizeObserver = new ResizeObserver(() => {
7520
+ checkScrollPosition();
7521
+ });
7522
+ resizeObserver.observe(container);
7523
+ return () => {
7524
+ resizeObserver.disconnect();
7525
+ };
7526
+ }, [children, checkScrollPosition]);
7527
+ // Handle scroll event
7528
+ useEffect(() => {
7529
+ const container = scrollRef.current;
7530
+ if (!container)
7531
+ return;
7532
+ const handleScroll = () => {
7533
+ checkScrollPosition();
7534
+ };
7535
+ container.addEventListener('scroll', handleScroll, { passive: true });
7536
+ return () => {
7537
+ container.removeEventListener('scroll', handleScroll);
7538
+ };
7539
+ }, [checkScrollPosition]);
7540
+ // Scroll by one item
7541
+ const scrollByItem = (direction) => {
7542
+ const container = scrollRef.current;
7543
+ if (!container)
7544
+ return;
7545
+ const children = Array.from(container.children);
7546
+ if (children.length === 0)
7547
+ return;
7548
+ const firstChild = children[0];
7549
+ const itemWidth = firstChild.offsetWidth;
7550
+ const gapValue = typeof gap === 'number' ? gap :
7551
+ gap === 'sm' ? 8 : gap === 'md' ? 16 : gap === 'lg' ? 24 : 0;
7552
+ const scrollAmount = itemWidth + gapValue;
7553
+ container.scrollBy({
7554
+ left: direction === 'left' ? -scrollAmount : scrollAmount,
7555
+ behavior: scrollBehavior,
7556
+ });
7557
+ };
7558
+ // Scroll to specific index
7559
+ const scrollToIndex = (index) => {
7560
+ const container = scrollRef.current;
7561
+ if (!container)
7562
+ return;
7563
+ const children = Array.from(container.children);
7564
+ if (index < 0 || index >= children.length)
7565
+ return;
7566
+ const child = children[index];
7567
+ child.scrollIntoView({
7568
+ behavior: scrollBehavior,
7569
+ block: 'nearest',
7570
+ inline: 'center',
7571
+ });
7572
+ };
7573
+ const showLeftArrow = showArrows === 'always' || (showArrows === 'hover' && isHovered);
7574
+ const showRightArrow = showArrows === 'always' || (showArrows === 'hover' && isHovered);
7575
+ return (jsxs("div", { className: `relative ${className}`, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), children: [showLeftArrow && canScrollLeft && (jsx("button", { onClick: () => scrollByItem('left'), className: "\n absolute left-0 top-1/2 -translate-y-1/2 z-10\n w-10 h-10 flex items-center justify-center\n bg-white/90 backdrop-blur-sm rounded-full shadow-lg\n text-ink-600 hover:text-ink-900 hover:bg-white\n transition-all duration-200\n -ml-2\n ", "aria-label": "Scroll left", children: jsx(ChevronLeft, { className: "h-5 w-5" }) })), jsx("div", { ref: scrollRef, className: `
7576
+ flex overflow-x-auto scrollbar-hide
7577
+ ${gapClass}
7578
+ ${snapToItem ? 'snap-x snap-mandatory' : ''}
7579
+ ${scrollClassName}
7580
+ `, style: {
7581
+ ...gapStyle,
7582
+ paddingRight: peekAmount > 0 ? `${peekAmount}px` : undefined,
7583
+ scrollPaddingLeft: '0px',
7584
+ scrollPaddingRight: `${peekAmount}px`,
7585
+ }, children: React__default.Children.map(children, (child, index) => (jsx("div", { className: `flex-shrink-0 ${snapToItem ? 'snap-start' : ''}`, children: child }, index))) }), showRightArrow && canScrollRight && (jsx("button", { onClick: () => scrollByItem('right'), className: "\n absolute right-0 top-1/2 -translate-y-1/2 z-10\n w-10 h-10 flex items-center justify-center\n bg-white/90 backdrop-blur-sm rounded-full shadow-lg\n text-ink-600 hover:text-ink-900 hover:bg-white\n transition-all duration-200\n -mr-2\n ", "aria-label": "Scroll right", children: jsx(ChevronRight, { className: "h-5 w-5" }) })), showIndicators && itemCount > 1 && (jsx("div", { className: "flex justify-center gap-1.5 mt-3", children: Array.from({ length: itemCount }).map((_, index) => (jsx("button", { onClick: () => scrollToIndex(index), className: `
7586
+ w-2 h-2 rounded-full transition-all duration-200
7587
+ ${index === activeIndex
7588
+ ? 'bg-accent-500 w-4'
7589
+ : 'bg-paper-300 hover:bg-paper-400'}
7590
+ `, "aria-label": `Go to item ${index + 1}`, "aria-current": index === activeIndex ? 'true' : 'false' }, index))) }))] }));
7591
+ }
7592
+
7593
+ /**
7594
+ * SwipeableCard - Card component with swipe-to-action functionality
7595
+ *
7596
+ * Designed for mobile approval workflows:
7597
+ * - Swipe right to approve/confirm
7598
+ * - Swipe left to see options/alternatives
7599
+ * - Visual feedback showing action being revealed
7600
+ * - Haptic feedback on mobile devices
7601
+ *
7602
+ * @example
7603
+ * ```tsx
7604
+ * <SwipeableCard
7605
+ * onSwipeRight={() => handleApprove()}
7606
+ * onSwipeLeft={() => handleShowOptions()}
7607
+ * rightAction={{
7608
+ * icon: <Check />,
7609
+ * color: 'success',
7610
+ * label: 'Approve'
7611
+ * }}
7612
+ * leftAction={{
7613
+ * icon: <MoreHorizontal />,
7614
+ * color: 'neutral',
7615
+ * label: 'Options'
7616
+ * }}
7617
+ * >
7618
+ * <TransactionContent />
7619
+ * </SwipeableCard>
7620
+ * ```
7621
+ */
7622
+ function SwipeableCard({ children, onSwipeRight, onSwipeLeft, rightAction = {
7623
+ icon: jsx(Check, { className: "h-6 w-6" }),
7624
+ color: 'success',
7625
+ label: 'Approve',
7626
+ }, leftAction = {
7627
+ icon: jsx(MoreHorizontal, { className: "h-6 w-6" }),
7628
+ color: 'neutral',
7629
+ label: 'Options',
7630
+ }, swipeThreshold = 100, hapticFeedback = true, disabled = false, onSwipeStart, onSwipeEnd, className = '', }) {
7631
+ const cardRef = useRef(null);
7632
+ const [isDragging, setIsDragging] = useState(false);
7633
+ const [offsetX, setOffsetX] = useState(0);
7634
+ const [isTriggered, setIsTriggered] = useState(null);
7635
+ const startX = useRef(0);
7636
+ const startY = useRef(0);
7637
+ const isHorizontalSwipe = useRef(null);
7638
+ // Color classes for action backgrounds
7639
+ const colorClasses = {
7640
+ success: 'bg-success-500',
7641
+ error: 'bg-error-500',
7642
+ warning: 'bg-warning-500',
7643
+ neutral: 'bg-paper-400',
7644
+ primary: 'bg-accent-500',
7645
+ };
7646
+ // Trigger haptic feedback
7647
+ const triggerHaptic = useCallback((style = 'medium') => {
7648
+ if (!hapticFeedback)
7649
+ return;
7650
+ // Use Vibration API if available
7651
+ if ('vibrate' in navigator) {
7652
+ const patterns = {
7653
+ light: 10,
7654
+ medium: 25,
7655
+ heavy: [50, 30, 50],
7656
+ };
7657
+ navigator.vibrate(patterns[style]);
7658
+ }
7659
+ }, [hapticFeedback]);
7660
+ // Handle drag start
7661
+ const handleDragStart = useCallback((clientX, clientY) => {
7662
+ if (disabled)
7663
+ return;
7664
+ setIsDragging(true);
7665
+ startX.current = clientX;
7666
+ startY.current = clientY;
7667
+ isHorizontalSwipe.current = null;
7668
+ onSwipeStart?.();
7669
+ }, [disabled, onSwipeStart]);
7670
+ // Handle drag move
7671
+ const handleDragMove = useCallback((clientX, clientY) => {
7672
+ if (!isDragging || disabled)
7673
+ return;
7674
+ const deltaX = clientX - startX.current;
7675
+ const deltaY = clientY - startY.current;
7676
+ // Determine if this is a horizontal swipe on first significant movement
7677
+ if (isHorizontalSwipe.current === null) {
7678
+ const absDeltaX = Math.abs(deltaX);
7679
+ const absDeltaY = Math.abs(deltaY);
7680
+ if (absDeltaX > 10 || absDeltaY > 10) {
7681
+ isHorizontalSwipe.current = absDeltaX > absDeltaY;
7682
+ }
7683
+ }
7684
+ // Only process horizontal swipes
7685
+ if (isHorizontalSwipe.current !== true)
7686
+ return;
7687
+ // Check if we should allow this direction
7688
+ const canSwipeRight = onSwipeRight !== undefined;
7689
+ const canSwipeLeft = onSwipeLeft !== undefined;
7690
+ let newOffset = deltaX;
7691
+ // Limit swipe direction based on available actions
7692
+ if (!canSwipeRight && deltaX > 0)
7693
+ newOffset = 0;
7694
+ if (!canSwipeLeft && deltaX < 0)
7695
+ newOffset = 0;
7696
+ // Add resistance when exceeding threshold
7697
+ const maxSwipe = swipeThreshold * 1.5;
7698
+ if (Math.abs(newOffset) > swipeThreshold) {
7699
+ const overflow = Math.abs(newOffset) - swipeThreshold;
7700
+ const resistance = overflow * 0.3;
7701
+ newOffset = newOffset > 0
7702
+ ? swipeThreshold + resistance
7703
+ : -(swipeThreshold + resistance);
7704
+ newOffset = Math.max(-maxSwipe, Math.min(maxSwipe, newOffset));
7705
+ }
7706
+ setOffsetX(newOffset);
7707
+ // Check for threshold crossing and trigger haptic
7708
+ const newTriggered = Math.abs(newOffset) >= swipeThreshold
7709
+ ? (newOffset > 0 ? 'right' : 'left')
7710
+ : null;
7711
+ if (newTriggered !== isTriggered) {
7712
+ if (newTriggered) {
7713
+ triggerHaptic('medium');
7714
+ }
7715
+ setIsTriggered(newTriggered);
7716
+ }
7717
+ }, [isDragging, disabled, onSwipeRight, onSwipeLeft, swipeThreshold, isTriggered, triggerHaptic]);
7718
+ // Handle drag end
7719
+ const handleDragEnd = useCallback(() => {
7720
+ if (!isDragging)
7721
+ return;
7722
+ setIsDragging(false);
7723
+ onSwipeEnd?.();
7724
+ // Check if action should be triggered
7725
+ if (Math.abs(offsetX) >= swipeThreshold) {
7726
+ if (offsetX > 0 && onSwipeRight) {
7727
+ triggerHaptic('heavy');
7728
+ // Animate card away then call handler
7729
+ setOffsetX(window.innerWidth);
7730
+ setTimeout(() => {
7731
+ onSwipeRight();
7732
+ setOffsetX(0);
7733
+ setIsTriggered(null);
7734
+ }, 200);
7735
+ return;
7736
+ }
7737
+ else if (offsetX < 0 && onSwipeLeft) {
7738
+ triggerHaptic('heavy');
7739
+ setOffsetX(-window.innerWidth);
7740
+ setTimeout(() => {
7741
+ onSwipeLeft();
7742
+ setOffsetX(0);
7743
+ setIsTriggered(null);
7744
+ }, 200);
7745
+ return;
7746
+ }
7747
+ }
7748
+ // Snap back
7749
+ setOffsetX(0);
7750
+ setIsTriggered(null);
7751
+ }, [isDragging, offsetX, swipeThreshold, onSwipeRight, onSwipeLeft, onSwipeEnd, triggerHaptic]);
7752
+ // Touch event handlers
7753
+ const handleTouchStart = (e) => {
7754
+ handleDragStart(e.touches[0].clientX, e.touches[0].clientY);
7755
+ };
7756
+ const handleTouchMove = (e) => {
7757
+ handleDragMove(e.touches[0].clientX, e.touches[0].clientY);
7758
+ // Prevent vertical scroll if horizontal swipe
7759
+ if (isHorizontalSwipe.current === true) {
7760
+ e.preventDefault();
7761
+ }
7762
+ };
7763
+ const handleTouchEnd = () => {
7764
+ handleDragEnd();
7765
+ };
7766
+ // Mouse event handlers (for desktop testing)
7767
+ const handleMouseDown = (e) => {
7768
+ handleDragStart(e.clientX, e.clientY);
7769
+ };
7770
+ useEffect(() => {
7771
+ if (!isDragging)
7772
+ return;
7773
+ const handleMouseMove = (e) => {
7774
+ handleDragMove(e.clientX, e.clientY);
7775
+ };
7776
+ const handleMouseUp = () => {
7777
+ handleDragEnd();
7778
+ };
7779
+ document.addEventListener('mousemove', handleMouseMove);
7780
+ document.addEventListener('mouseup', handleMouseUp);
7781
+ return () => {
7782
+ document.removeEventListener('mousemove', handleMouseMove);
7783
+ document.removeEventListener('mouseup', handleMouseUp);
7784
+ };
7785
+ }, [isDragging, handleDragMove, handleDragEnd]);
7786
+ // Calculate action opacity based on swipe distance
7787
+ const rightActionOpacity = offsetX > 0 ? Math.min(1, offsetX / swipeThreshold) : 0;
7788
+ const leftActionOpacity = offsetX < 0 ? Math.min(1, Math.abs(offsetX) / swipeThreshold) : 0;
7789
+ return (jsxs("div", { className: `relative overflow-hidden rounded-lg ${className}`, children: [onSwipeRight && (jsx("div", { className: `
7790
+ absolute inset-y-0 left-0 flex items-center justify-start pl-6
7791
+ ${colorClasses[rightAction.color]}
7792
+ transition-opacity duration-100
7793
+ `, style: {
7794
+ opacity: rightActionOpacity,
7795
+ width: Math.abs(offsetX) + 20,
7796
+ }, "aria-hidden": "true", children: jsx("div", { className: `
7797
+ text-white transform transition-transform duration-200
7798
+ ${isTriggered === 'right' ? 'scale-125' : 'scale-100'}
7799
+ `, children: rightAction.icon }) })), onSwipeLeft && (jsx("div", { className: `
7800
+ absolute inset-y-0 right-0 flex items-center justify-end pr-6
7801
+ ${colorClasses[leftAction.color]}
7802
+ transition-opacity duration-100
7803
+ `, style: {
7804
+ opacity: leftActionOpacity,
7805
+ width: Math.abs(offsetX) + 20,
7806
+ }, "aria-hidden": "true", children: jsx("div", { className: `
7807
+ text-white transform transition-transform duration-200
7808
+ ${isTriggered === 'left' ? 'scale-125' : 'scale-100'}
7809
+ `, children: leftAction.icon }) })), jsx("div", { ref: cardRef, className: `
7810
+ relative bg-white
7811
+ ${isDragging ? '' : 'transition-transform duration-200 ease-out'}
7812
+ ${disabled ? 'opacity-50 pointer-events-none' : ''}
7813
+ `, style: {
7814
+ transform: `translateX(${offsetX}px)`,
7815
+ }, onTouchStart: handleTouchStart, onTouchMove: handleTouchMove, onTouchEnd: handleTouchEnd, onMouseDown: handleMouseDown, role: "button", "aria-label": `Swipeable card. ${onSwipeRight ? `Swipe right to ${rightAction.label}.` : ''} ${onSwipeLeft ? `Swipe left to ${leftAction.label}.` : ''}`, tabIndex: disabled ? -1 : 0, children: children })] }));
7816
+ }
7817
+
7818
+ /**
7819
+ * NotificationBanner - Dismissible banner for important alerts
7820
+ *
7821
+ * Displays at top of screen for alerts that need attention but aren't blocking:
7822
+ * - Money Found alerts
7823
+ * - System messages
7824
+ * - Promotional info
7825
+ *
7826
+ * @example
7827
+ * ```tsx
7828
+ * <NotificationBanner
7829
+ * variant="warning"
7830
+ * icon={<DollarSign />}
7831
+ * title="Found $33.98 in potential savings"
7832
+ * description="Tap to review"
7833
+ * action={{
7834
+ * label: "Review",
7835
+ * onClick: handleReview
7836
+ * }}
7837
+ * onDismiss={() => setShowBanner(false)}
7838
+ * />
7839
+ * ```
7840
+ */
7841
+ function NotificationBanner({ variant = 'info', icon, title, description, action, onDismiss, dismissible = true, sticky = false, className = '', }) {
7842
+ const bannerRef = useRef(null);
7843
+ const [isDragging, setIsDragging] = useState(false);
7844
+ const [offsetX, setOffsetX] = useState(0);
7845
+ const [isDismissed, setIsDismissed] = useState(false);
7846
+ const startX = useRef(0);
7847
+ // Default icons based on variant
7848
+ const defaultIcons = {
7849
+ info: jsx(Info, { className: "h-5 w-5" }),
7850
+ success: jsx(CheckCircle, { className: "h-5 w-5" }),
7851
+ warning: jsx(AlertTriangle, { className: "h-5 w-5" }),
7852
+ error: jsx(AlertCircle, { className: "h-5 w-5" }),
7853
+ };
7854
+ // Color classes
7855
+ const variantClasses = {
7856
+ info: 'bg-gradient-to-r from-primary-50 to-primary-100 border-primary-200 text-primary-900',
7857
+ success: 'bg-gradient-to-r from-success-50 to-success-100 border-success-200 text-success-900',
7858
+ warning: 'bg-gradient-to-r from-warning-50 to-warning-100 border-warning-200 text-warning-900',
7859
+ error: 'bg-gradient-to-r from-error-50 to-error-100 border-error-200 text-error-900',
7860
+ };
7861
+ const iconColorClasses = {
7862
+ info: 'text-primary-600',
7863
+ success: 'text-success-600',
7864
+ warning: 'text-warning-600',
7865
+ error: 'text-error-600',
7866
+ };
7867
+ const buttonClasses = {
7868
+ info: 'bg-primary-600 hover:bg-primary-700 text-white',
7869
+ success: 'bg-success-600 hover:bg-success-700 text-white',
7870
+ warning: 'bg-warning-600 hover:bg-warning-700 text-white',
7871
+ error: 'bg-error-600 hover:bg-error-700 text-white',
7872
+ };
7873
+ // Handle swipe dismiss
7874
+ const handleDragStart = useCallback((clientX) => {
7875
+ if (!dismissible)
7876
+ return;
7877
+ setIsDragging(true);
7878
+ startX.current = clientX;
7879
+ }, [dismissible]);
7880
+ const handleDragMove = useCallback((clientX) => {
7881
+ if (!isDragging)
7882
+ return;
7883
+ const delta = clientX - startX.current;
7884
+ setOffsetX(delta);
7885
+ }, [isDragging]);
7886
+ const handleDragEnd = useCallback(() => {
7887
+ if (!isDragging)
7888
+ return;
7889
+ setIsDragging(false);
7890
+ const threshold = 100;
7891
+ if (Math.abs(offsetX) > threshold) {
7892
+ // Animate out
7893
+ setOffsetX(offsetX > 0 ? window.innerWidth : -window.innerWidth);
7894
+ setIsDismissed(true);
7895
+ setTimeout(() => {
7896
+ onDismiss?.();
7897
+ }, 200);
7898
+ }
7899
+ else {
7900
+ // Snap back
7901
+ setOffsetX(0);
7902
+ }
7903
+ }, [isDragging, offsetX, onDismiss]);
7904
+ // Touch handlers
7905
+ const handleTouchStart = (e) => {
7906
+ handleDragStart(e.touches[0].clientX);
7907
+ };
7908
+ const handleTouchMove = (e) => {
7909
+ handleDragMove(e.touches[0].clientX);
7910
+ };
7911
+ const handleTouchEnd = () => {
7912
+ handleDragEnd();
7913
+ };
7914
+ // Mouse handlers for desktop testing
7915
+ const handleMouseDown = (e) => {
7916
+ if (dismissible) {
7917
+ handleDragStart(e.clientX);
7918
+ }
7919
+ };
7920
+ useEffect(() => {
7921
+ if (!isDragging)
7922
+ return;
7923
+ const handleMouseMove = (e) => {
7924
+ handleDragMove(e.clientX);
7925
+ };
7926
+ const handleMouseUp = () => {
7927
+ handleDragEnd();
7928
+ };
7929
+ document.addEventListener('mousemove', handleMouseMove);
7930
+ document.addEventListener('mouseup', handleMouseUp);
7931
+ return () => {
7932
+ document.removeEventListener('mousemove', handleMouseMove);
7933
+ document.removeEventListener('mouseup', handleMouseUp);
7934
+ };
7935
+ }, [isDragging, handleDragMove, handleDragEnd]);
7936
+ if (isDismissed)
7937
+ return null;
7938
+ return (jsx("div", { ref: bannerRef, className: `
7939
+ w-full border-b
7940
+ ${variantClasses[variant]}
7941
+ ${sticky ? 'sticky top-0 z-40' : ''}
7942
+ ${isDragging ? '' : 'transition-transform duration-200 ease-out'}
7943
+ ${className}
7944
+ `, style: {
7945
+ transform: `translateX(${offsetX}px)`,
7946
+ opacity: Math.max(0, 1 - Math.abs(offsetX) / 200),
7947
+ }, onTouchStart: handleTouchStart, onTouchMove: handleTouchMove, onTouchEnd: handleTouchEnd, onMouseDown: handleMouseDown, role: "alert", children: jsxs("div", { className: "flex items-center gap-3 px-4 py-3", children: [jsx("div", { className: `flex-shrink-0 ${iconColorClasses[variant]}`, children: icon || defaultIcons[variant] }), jsxs("div", { className: "flex-1 min-w-0", children: [jsx("p", { className: "text-sm font-medium truncate", children: title }), description && (jsx("p", { className: "text-xs opacity-80 truncate", children: description }))] }), action && (jsx("button", { onClick: action.onClick, className: `
7948
+ flex-shrink-0 px-3 py-1.5 text-xs font-medium rounded-md
7949
+ transition-colors duration-200
7950
+ ${buttonClasses[variant]}
7951
+ `, children: action.label })), onDismiss && (jsx("button", { onClick: onDismiss, className: "flex-shrink-0 p-1 rounded-full hover:bg-black/10 transition-colors duration-200", "aria-label": "Dismiss notification", children: jsx(X, { className: "h-4 w-4" }) }))] }) }));
7952
+ }
7953
+
7954
+ /**
7955
+ * CompactStat - Single stat display optimized for mobile
7956
+ *
7957
+ * Designed for dashboard stats in 2-column mobile layouts:
7958
+ * - Compact presentation with value, label, and optional trend
7959
+ * - Responsive sizing
7960
+ * - Trend indicators with color coding
7961
+ *
7962
+ * @example
7963
+ * ```tsx
7964
+ * <Grid columns={2} gap="sm">
7965
+ * <CompactStat
7966
+ * value="$62,329"
7967
+ * label="Net Worth"
7968
+ * trend={{
7969
+ * direction: 'up',
7970
+ * value: '+$1,247',
7971
+ * color: 'success'
7972
+ * }}
7973
+ * />
7974
+ * <CompactStat
7975
+ * value="$4,521"
7976
+ * label="Monthly Income"
7977
+ * />
7978
+ * </Grid>
7979
+ * ```
7980
+ */
7981
+ function CompactStat({ value, label, trend, size = 'md', align = 'left', className = '', }) {
7982
+ // Size classes
7983
+ const sizeClasses = {
7984
+ sm: {
7985
+ value: 'text-lg font-semibold',
7986
+ label: 'text-xs',
7987
+ trend: 'text-xs',
7988
+ icon: 'h-3 w-3',
7989
+ },
7990
+ md: {
7991
+ value: 'text-xl font-semibold',
7992
+ label: 'text-sm',
7993
+ trend: 'text-xs',
7994
+ icon: 'h-3.5 w-3.5',
7995
+ },
7996
+ lg: {
7997
+ value: 'text-2xl font-bold',
7998
+ label: 'text-sm',
7999
+ trend: 'text-sm',
8000
+ icon: 'h-4 w-4',
8001
+ },
8002
+ };
8003
+ // Alignment classes
8004
+ const alignClasses = {
8005
+ left: 'text-left',
8006
+ center: 'text-center',
8007
+ right: 'text-right',
8008
+ };
8009
+ // Trend color classes
8010
+ const getTrendColor = (trend) => {
8011
+ if (trend.color) {
8012
+ const colorMap = {
8013
+ success: 'text-success-600',
8014
+ error: 'text-error-600',
8015
+ warning: 'text-warning-600',
8016
+ neutral: 'text-ink-500',
8017
+ };
8018
+ return colorMap[trend.color];
8019
+ }
8020
+ // Default colors based on direction
8021
+ const directionColors = {
8022
+ up: 'text-success-600',
8023
+ down: 'text-error-600',
8024
+ neutral: 'text-ink-500',
8025
+ };
8026
+ return directionColors[trend.direction];
8027
+ };
8028
+ // Trend icons
8029
+ const TrendIcon = trend ? {
8030
+ up: TrendingUp,
8031
+ down: TrendingDown,
8032
+ neutral: Minus,
8033
+ }[trend.direction] : null;
8034
+ const sizes = sizeClasses[size];
8035
+ return (jsxs("div", { className: `${alignClasses[align]} ${className}`, children: [jsx("div", { className: `${sizes.value} text-ink-900 tracking-tight`, children: value }), jsx("div", { className: `${sizes.label} text-ink-500 mt-0.5`, children: label }), trend && (jsxs("div", { className: `
8036
+ flex items-center gap-1 mt-1
8037
+ ${align === 'center' ? 'justify-center' : ''}
8038
+ ${align === 'right' ? 'justify-end' : ''}
8039
+ ${sizes.trend} ${getTrendColor(trend)}
8040
+ `, children: [TrendIcon && jsx(TrendIcon, { className: sizes.icon }), jsx("span", { children: trend.value })] }))] }));
8041
+ }
8042
+
7341
8043
  /**
7342
8044
  * Hook to detect breadcrumb navigation and trigger callbacks.
7343
8045
  * Use this in host components to reset state when a breadcrumb is clicked.
@@ -7621,7 +8323,7 @@ function StepIndicator({ steps, currentStep, variant = 'horizontal', onStepClick
7621
8323
  }) }) }));
7622
8324
  }
7623
8325
 
7624
- function Badge({ children, variant = 'neutral', size = 'md', icon, onRemove, className = '', dot = false, }) {
8326
+ function Badge({ children, variant = 'neutral', size = 'md', icon, onRemove, className = '', dot = false, pill = false, }) {
7625
8327
  const variantStyles = {
7626
8328
  success: 'bg-success-50 text-success-700 border-success-200',
7627
8329
  warning: 'bg-warning-50 text-warning-700 border-warning-200',
@@ -7641,6 +8343,12 @@ function Badge({ children, variant = 'neutral', size = 'md', icon, onRemove, cla
7641
8343
  md: 'px-3 py-1 text-xs gap-1.5',
7642
8344
  lg: 'px-3 py-1.5 text-sm gap-2',
7643
8345
  };
8346
+ // Pill variant has tighter horizontal padding and fully rounded ends
8347
+ const pillSizeStyles = {
8348
+ sm: 'px-1.5 py-0.5 text-xs gap-1',
8349
+ md: 'px-2 py-0.5 text-xs gap-1',
8350
+ lg: 'px-2.5 py-1 text-sm gap-1.5',
8351
+ };
7644
8352
  const dotSizeStyles = {
7645
8353
  sm: 'h-1.5 w-1.5',
7646
8354
  md: 'h-2 w-2',
@@ -7662,9 +8370,10 @@ function Badge({ children, variant = 'neutral', size = 'md', icon, onRemove, cla
7662
8370
  }
7663
8371
  // Regular badge
7664
8372
  return (jsxs("span", { className: `
7665
- inline-flex items-center rounded-full border font-medium
8373
+ inline-flex items-center border font-medium
8374
+ ${pill ? 'rounded-full' : 'rounded-full'}
7666
8375
  ${variantStyles[variant]}
7667
- ${sizeStyles[size]}
8376
+ ${pill ? pillSizeStyles[size] : sizeStyles[size]}
7668
8377
  ${className}
7669
8378
  `, children: [icon && jsx("span", { className: iconSize[size], children: icon }), jsx("span", { children: children }), onRemove && (jsx("button", { onClick: onRemove, className: "ml-1 hover:opacity-70 transition-opacity", "aria-label": "Remove badge", children: jsx(X, { className: iconSize[size] }) }))] }));
7670
8379
  }
@@ -7772,8 +8481,10 @@ function Progress({ value, variant = 'linear', size = 'md', color = 'primary', s
7772
8481
  warning: 'bg-warning-100',
7773
8482
  error: 'bg-error-100',
7774
8483
  };
8484
+ // Normalize 'ring' to 'circular'
8485
+ const normalizedVariant = variant === 'ring' ? 'circular' : variant;
7775
8486
  // Linear progress
7776
- if (variant === 'linear') {
8487
+ if (normalizedVariant === 'linear') {
7777
8488
  const heightClasses = {
7778
8489
  sm: 'h-1',
7779
8490
  md: 'h-2',
@@ -8705,94 +9416,112 @@ function useFABScroll(threshold = 10) {
8705
9416
  }
8706
9417
 
8707
9418
  /**
8708
- * PullToRefresh - Mobile pull-to-refresh gesture handler
9419
+ * PullToRefresh - Pull-down refresh indicator and handler for mobile lists
8709
9420
  *
8710
- * Wraps content and provides native-feeling pull-to-refresh functionality.
8711
- * Only activates when scrolled to top of content.
8712
- *
8713
- * @example Basic usage
8714
- * ```tsx
8715
- * <PullToRefresh onRefresh={async () => {
8716
- * await fetchLatestData();
8717
- * }}>
8718
- * <div className="min-h-screen">
8719
- * {content}
8720
- * </div>
8721
- * </PullToRefresh>
8722
- * ```
9421
+ * Wraps content to enable pull-to-refresh behavior on mobile:
9422
+ * - Pull down to trigger refresh
9423
+ * - Visual feedback showing progress
9424
+ * - Custom content for each state
8723
9425
  *
8724
- * @example With custom threshold
9426
+ * @example
8725
9427
  * ```tsx
8726
- * <PullToRefresh
8727
- * onRefresh={handleRefresh}
8728
- * pullThreshold={100}
8729
- * maxPull={150}
8730
- * >
8731
- * {content}
9428
+ * <PullToRefresh onRefresh={async () => { await syncData(); }}>
9429
+ * <TransactionList transactions={transactions} />
8732
9430
  * </PullToRefresh>
8733
9431
  * ```
8734
9432
  */
8735
- function PullToRefresh({ children, onRefresh, disabled = false, pullThreshold = 80, maxPull = 120, loadingIndicator, pullIndicator, className = '', }) {
9433
+ function PullToRefresh({ children, onRefresh, threshold = 80, disabled = false, pullingContent, releaseContent, refreshingContent, completeContent, className = '', }) {
9434
+ const containerRef = useRef(null);
8736
9435
  const [state, setState] = useState('idle');
8737
9436
  const [pullDistance, setPullDistance] = useState(0);
8738
- const containerRef = useRef(null);
8739
9437
  const startY = useRef(0);
8740
9438
  const currentY = useRef(0);
8741
- // Check if at top of scroll container
9439
+ const isDragging = useRef(false);
9440
+ // Check if content is at top (can pull to refresh)
8742
9441
  const isAtTop = useCallback(() => {
8743
9442
  const container = containerRef.current;
8744
9443
  if (!container)
8745
9444
  return false;
8746
- return container.scrollTop <= 0;
9445
+ // Check if the scrollable content is at the top
9446
+ const scrollableParent = container.querySelector('[data-ptr-scrollable]') || container;
9447
+ return scrollableParent.scrollTop <= 0;
8747
9448
  }, []);
8748
- // Handle touch start
9449
+ // Handle pull start
8749
9450
  const handleTouchStart = useCallback((e) => {
8750
- if (disabled || state === 'refreshing' || !isAtTop())
9451
+ if (disabled || state === 'refreshing')
9452
+ return;
9453
+ if (!isAtTop())
8751
9454
  return;
9455
+ isDragging.current = true;
8752
9456
  startY.current = e.touches[0].clientY;
8753
- currentY.current = startY.current;
9457
+ currentY.current = e.touches[0].clientY;
8754
9458
  }, [disabled, state, isAtTop]);
8755
- // Handle touch move
9459
+ // Handle pull move
8756
9460
  const handleTouchMove = useCallback((e) => {
8757
- if (disabled || state === 'refreshing')
8758
- return;
8759
- if (startY.current === 0)
9461
+ if (!isDragging.current || disabled || state === 'refreshing')
8760
9462
  return;
8761
9463
  currentY.current = e.touches[0].clientY;
8762
- const diff = currentY.current - startY.current;
8763
- // Only allow pulling down when at top
8764
- if (diff > 0 && isAtTop()) {
8765
- // Apply resistance - pull slows down as distance increases
8766
- const resistance = 0.5;
8767
- const adjustedPull = Math.min(diff * resistance, maxPull);
8768
- setPullDistance(adjustedPull);
8769
- setState(adjustedPull >= pullThreshold ? 'ready' : 'pulling');
8770
- // Prevent default scroll when pulling
8771
- if (adjustedPull > 0) {
8772
- e.preventDefault();
8773
- }
9464
+ const delta = currentY.current - startY.current;
9465
+ // Only activate pull-to-refresh when pulling down
9466
+ if (delta < 0) {
9467
+ isDragging.current = false;
9468
+ setPullDistance(0);
9469
+ setState('idle');
9470
+ return;
9471
+ }
9472
+ // Check if we're at the top before allowing pull
9473
+ if (!isAtTop()) {
9474
+ isDragging.current = false;
9475
+ return;
9476
+ }
9477
+ // Apply resistance to pull
9478
+ const resistance = 0.5;
9479
+ const resistedDelta = delta * resistance;
9480
+ const maxPull = threshold * 2;
9481
+ const clampedDelta = Math.min(resistedDelta, maxPull);
9482
+ setPullDistance(clampedDelta);
9483
+ // Update state based on pull distance
9484
+ if (clampedDelta >= threshold) {
9485
+ setState('ready');
9486
+ }
9487
+ else if (clampedDelta > 0) {
9488
+ setState('pulling');
9489
+ }
9490
+ // Prevent default scroll when pulling
9491
+ if (delta > 0 && isAtTop()) {
9492
+ e.preventDefault();
8774
9493
  }
8775
- }, [disabled, state, isAtTop, maxPull, pullThreshold]);
8776
- // Handle touch end
9494
+ }, [disabled, state, threshold, isAtTop]);
9495
+ // Handle pull end
8777
9496
  const handleTouchEnd = useCallback(async () => {
8778
- if (disabled || state === 'refreshing')
9497
+ if (!isDragging.current)
8779
9498
  return;
8780
- if (state === 'ready') {
9499
+ isDragging.current = false;
9500
+ if (state === 'ready' && pullDistance >= threshold) {
8781
9501
  setState('refreshing');
8782
- setPullDistance(pullThreshold); // Hold at threshold while refreshing
9502
+ setPullDistance(threshold * 0.6); // Settle at a smaller height while refreshing
8783
9503
  try {
8784
9504
  await onRefresh();
9505
+ setState('complete');
9506
+ // Show complete state briefly
9507
+ setTimeout(() => {
9508
+ setState('idle');
9509
+ setPullDistance(0);
9510
+ }, 500);
8785
9511
  }
8786
9512
  catch (error) {
8787
9513
  console.error('Refresh failed:', error);
9514
+ setState('idle');
9515
+ setPullDistance(0);
8788
9516
  }
9517
+ }
9518
+ else {
9519
+ // Snap back
8789
9520
  setState('idle');
9521
+ setPullDistance(0);
8790
9522
  }
8791
- setPullDistance(0);
8792
- startY.current = 0;
8793
- currentY.current = 0;
8794
- }, [disabled, state, pullThreshold, onRefresh]);
8795
- // Attach touch listeners
9523
+ }, [state, pullDistance, threshold, onRefresh]);
9524
+ // Attach touch event listeners
8796
9525
  useEffect(() => {
8797
9526
  const container = containerRef.current;
8798
9527
  if (!container)
@@ -8806,99 +9535,41 @@ function PullToRefresh({ children, onRefresh, disabled = false, pullThreshold =
8806
9535
  container.removeEventListener('touchend', handleTouchEnd);
8807
9536
  };
8808
9537
  }, [handleTouchStart, handleTouchMove, handleTouchEnd]);
8809
- // Calculate indicator opacity and rotation
8810
- const progress = Math.min(pullDistance / pullThreshold, 1);
8811
- const rotation = progress * 180;
8812
- // Default loading indicator
8813
- const defaultLoadingIndicator = (jsx(Loader2, { className: "h-6 w-6 text-accent-600 animate-spin" }));
8814
- // Default pull indicator
8815
- const defaultPullIndicator = (jsx("div", { className: `
8816
- transition-transform duration-200
8817
- ${state === 'ready' ? 'text-accent-600' : 'text-ink-400'}
8818
- `, style: { transform: `rotate(${rotation}deg)` }, children: jsx(ArrowDown, { className: "h-6 w-6" }) }));
8819
- return (jsxs("div", { ref: containerRef, className: `relative overflow-auto ${className}`, style: { touchAction: pullDistance > 0 ? 'none' : 'auto' }, children: [jsx("div", { className: `
8820
- absolute left-0 right-0 flex items-center justify-center
8821
- transition-all duration-200 overflow-hidden
8822
- ${state === 'idle' && pullDistance === 0 ? 'opacity-0' : 'opacity-100'}
9538
+ // Calculate progress percentage
9539
+ const progress = Math.min(1, pullDistance / threshold);
9540
+ // Default content for each state
9541
+ const defaultPullingContent = (jsxs("div", { className: "flex flex-col items-center gap-1", children: [jsx(ArrowDown, { className: "h-5 w-5 text-ink-400 transition-transform duration-200", style: { transform: `rotate(${progress * 180}deg)` } }), jsx("span", { className: "text-xs text-ink-500", children: "Pull to refresh" })] }));
9542
+ const defaultReleaseContent = (jsxs("div", { className: "flex flex-col items-center gap-1", children: [jsx(ArrowDown, { className: "h-5 w-5 text-accent-500 rotate-180" }), jsx("span", { className: "text-xs text-accent-600 font-medium", children: "Release to refresh" })] }));
9543
+ const defaultRefreshingContent = (jsxs("div", { className: "flex flex-col items-center gap-1", children: [jsx(Loader2, { className: "h-5 w-5 text-accent-500 animate-spin" }), jsx("span", { className: "text-xs text-ink-500", children: "Refreshing..." })] }));
9544
+ const defaultCompleteContent = (jsxs("div", { className: "flex flex-col items-center gap-1", children: [jsx(Check, { className: "h-5 w-5 text-success-500" }), jsx("span", { className: "text-xs text-success-600", children: "Done!" })] }));
9545
+ // Get content based on current state
9546
+ const getIndicatorContent = () => {
9547
+ switch (state) {
9548
+ case 'pulling':
9549
+ return pullingContent || defaultPullingContent;
9550
+ case 'ready':
9551
+ return releaseContent || defaultReleaseContent;
9552
+ case 'refreshing':
9553
+ return refreshingContent || defaultRefreshingContent;
9554
+ case 'complete':
9555
+ return completeContent || defaultCompleteContent;
9556
+ default:
9557
+ return null;
9558
+ }
9559
+ };
9560
+ return (jsxs("div", { ref: containerRef, className: `relative overflow-hidden ${className}`, children: [jsx("div", { className: `
9561
+ absolute top-0 left-0 right-0
9562
+ flex items-center justify-center
9563
+ bg-paper-50
9564
+ transition-all duration-200 ease-out
9565
+ ${state === 'idle' ? 'opacity-0' : 'opacity-100'}
8823
9566
  `, style: {
8824
- height: `${pullDistance}px`,
8825
- top: 0,
8826
- zIndex: 10,
8827
- }, children: jsx("div", { className: `
8828
- w-10 h-10 rounded-full bg-white shadow-md
8829
- flex items-center justify-center
8830
- transition-transform duration-200
8831
- ${state === 'refreshing' ? 'scale-100' : progress < 0.3 ? 'scale-75' : 'scale-100'}
8832
- `, children: state === 'refreshing'
8833
- ? (loadingIndicator || defaultLoadingIndicator)
8834
- : (pullIndicator || defaultPullIndicator) }) }), jsx("div", { className: "transition-transform duration-200", style: {
9567
+ height: pullDistance,
9568
+ transform: state === 'idle' ? 'translateY(-100%)' : 'translateY(0)',
9569
+ }, children: getIndicatorContent() }), jsx("div", { className: "transition-transform duration-200 ease-out", style: {
8835
9570
  transform: `translateY(${pullDistance}px)`,
8836
9571
  }, children: children })] }));
8837
9572
  }
8838
- /**
8839
- * usePullToRefresh - Hook for custom pull-to-refresh implementations
8840
- *
8841
- * @example
8842
- * ```tsx
8843
- * const { pullDistance, isRefreshing, bind } = usePullToRefresh({
8844
- * onRefresh: async () => {
8845
- * await fetchData();
8846
- * }
8847
- * });
8848
- *
8849
- * return (
8850
- * <div {...bind}>
8851
- * {isRefreshing && <Spinner />}
8852
- * {content}
8853
- * </div>
8854
- * );
8855
- * ```
8856
- */
8857
- function usePullToRefresh({ onRefresh, pullThreshold = 80, maxPull = 120, disabled = false, }) {
8858
- const [pullDistance, setPullDistance] = useState(0);
8859
- const [isRefreshing, setIsRefreshing] = useState(false);
8860
- const startY = useRef(0);
8861
- const handleTouchStart = useCallback((e) => {
8862
- if (disabled || isRefreshing)
8863
- return;
8864
- startY.current = e.touches[0].clientY;
8865
- }, [disabled, isRefreshing]);
8866
- const handleTouchMove = useCallback((e) => {
8867
- if (disabled || isRefreshing || startY.current === 0)
8868
- return;
8869
- const diff = e.touches[0].clientY - startY.current;
8870
- if (diff > 0) {
8871
- const adjustedPull = Math.min(diff * 0.5, maxPull);
8872
- setPullDistance(adjustedPull);
8873
- }
8874
- }, [disabled, isRefreshing, maxPull]);
8875
- const handleTouchEnd = useCallback(async () => {
8876
- if (disabled || isRefreshing)
8877
- return;
8878
- if (pullDistance >= pullThreshold) {
8879
- setIsRefreshing(true);
8880
- try {
8881
- await onRefresh();
8882
- }
8883
- finally {
8884
- setIsRefreshing(false);
8885
- }
8886
- }
8887
- setPullDistance(0);
8888
- startY.current = 0;
8889
- }, [disabled, isRefreshing, pullDistance, pullThreshold, onRefresh]);
8890
- return {
8891
- pullDistance,
8892
- isRefreshing,
8893
- isReady: pullDistance >= pullThreshold,
8894
- progress: Math.min(pullDistance / pullThreshold, 1),
8895
- bind: {
8896
- onTouchStart: handleTouchStart,
8897
- onTouchMove: handleTouchMove,
8898
- onTouchEnd: handleTouchEnd,
8899
- },
8900
- };
8901
- }
8902
9573
 
8903
9574
  function Logo({ size = 'md', showText = true, text = 'Commora', className = '', }) {
8904
9575
  const sizes = {
@@ -10401,44 +11072,52 @@ function getAugmentedNamespace(n) {
10401
11072
  * (A1, A1:C5, ...)
10402
11073
  */
10403
11074
 
10404
- let Collection$3 = class Collection {
11075
+ var collection;
11076
+ var hasRequiredCollection;
11077
+
11078
+ function requireCollection () {
11079
+ if (hasRequiredCollection) return collection;
11080
+ hasRequiredCollection = 1;
11081
+ class Collection {
10405
11082
 
10406
- constructor(data, refs) {
10407
- if (data == null && refs == null) {
10408
- this._data = [];
10409
- this._refs = [];
10410
- } else {
10411
- if (data.length !== refs.length)
10412
- throw Error('Collection: data length should match references length.');
10413
- this._data = data;
10414
- this._refs = refs;
10415
- }
10416
- }
11083
+ constructor(data, refs) {
11084
+ if (data == null && refs == null) {
11085
+ this._data = [];
11086
+ this._refs = [];
11087
+ } else {
11088
+ if (data.length !== refs.length)
11089
+ throw Error('Collection: data length should match references length.');
11090
+ this._data = data;
11091
+ this._refs = refs;
11092
+ }
11093
+ }
10417
11094
 
10418
- get data() {
10419
- return this._data;
10420
- }
11095
+ get data() {
11096
+ return this._data;
11097
+ }
10421
11098
 
10422
- get refs() {
10423
- return this._refs;
10424
- }
11099
+ get refs() {
11100
+ return this._refs;
11101
+ }
10425
11102
 
10426
- get length() {
10427
- return this._data.length;
10428
- }
11103
+ get length() {
11104
+ return this._data.length;
11105
+ }
10429
11106
 
10430
- /**
10431
- * Add data and references to this collection.
10432
- * @param {{}} obj - data
10433
- * @param {{}} ref - reference
10434
- */
10435
- add(obj, ref) {
10436
- this._data.push(obj);
10437
- this._refs.push(ref);
10438
- }
10439
- };
11107
+ /**
11108
+ * Add data and references to this collection.
11109
+ * @param {{}} obj - data
11110
+ * @param {{}} ref - reference
11111
+ */
11112
+ add(obj, ref) {
11113
+ this._data.push(obj);
11114
+ this._refs.push(ref);
11115
+ }
11116
+ }
10440
11117
 
10441
- var collection = Collection$3;
11118
+ collection = Collection;
11119
+ return collection;
11120
+ }
10442
11121
 
10443
11122
  var helpers;
10444
11123
  var hasRequiredHelpers;
@@ -10447,7 +11126,7 @@ function requireHelpers () {
10447
11126
  if (hasRequiredHelpers) return helpers;
10448
11127
  hasRequiredHelpers = 1;
10449
11128
  const FormulaError = requireError();
10450
- const Collection = collection;
11129
+ const Collection = requireCollection();
10451
11130
 
10452
11131
  const Types = {
10453
11132
  NUMBER: 0,
@@ -20101,7 +20780,7 @@ var engineering = EngineeringFunctions;
20101
20780
 
20102
20781
  const FormulaError$b = requireError();
20103
20782
  const {FormulaHelpers: FormulaHelpers$8, Types: Types$6, WildCard, Address: Address$3} = requireHelpers();
20104
- const Collection$2 = collection;
20783
+ const Collection$2 = requireCollection();
20105
20784
  const H$5 = FormulaHelpers$8;
20106
20785
 
20107
20786
  const ReferenceFunctions$1 = {
@@ -31729,7 +32408,7 @@ var parsing = {
31729
32408
  const FormulaError$4 = requireError();
31730
32409
  const {Address: Address$1} = requireHelpers();
31731
32410
  const {Prefix: Prefix$1, Postfix: Postfix$1, Infix: Infix$1, Operators: Operators$1} = operators;
31732
- const Collection$1 = collection;
32411
+ const Collection$1 = requireCollection();
31733
32412
  const MAX_ROW$1 = 1048576, MAX_COLUMN$1 = 16384;
31734
32413
  const {NotAllInputParsedException} = require$$4;
31735
32414
 
@@ -32491,7 +33170,7 @@ var hooks$1 = {
32491
33170
  const FormulaError$2 = requireError();
32492
33171
  const {FormulaHelpers: FormulaHelpers$1, Types, Address} = requireHelpers();
32493
33172
  const {Prefix, Postfix, Infix, Operators} = operators;
32494
- const Collection = collection;
33173
+ const Collection = requireCollection();
32495
33174
  const MAX_ROW = 1048576, MAX_COLUMN = 16384;
32496
33175
 
32497
33176
  let Utils$1 = class Utils {
@@ -56981,5 +57660,5 @@ function Responsive({ mobile, tablet, desktop, }) {
56981
57660
  return jsx(Fragment, { children: mobile || tablet || desktop });
56982
57661
  }
56983
57662
 
56984
- export { Accordion, ActionBar, ActionBarCenter, ActionBarLeft, ActionBarRight, ActionButton, AdminModal, Alert, AlertDialog, AppLayout, Autocomplete, Avatar, BREAKPOINTS, Badge, BottomNavigation, BottomNavigationSpacer, BottomSheet, Box, Breadcrumbs, Button, ButtonGroup, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CardView, Carousel, Checkbox, CheckboxList, Chip, ChipGroup, Collapsible, ColorPicker, Combobox, ComingSoon, CommandPalette, 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, HoverCard, InfiniteScroll, Input, KanbanBoard, Layout, Loading, LoadingOverlay, Logo, MarkdownEditor, MaskedInput, Menu, MenuDivider, MobileHeader, MobileHeaderSpacer, MobileLayout, MobileOnly, MobileProvider, Modal, ModalFooter, MultiSelect, NotificationBar, NotificationIndicator, NumberInput, Page, PageHeader, PageLayout, PageNavigation, Pagination, PasswordInput, Popover, Progress, PullToRefresh, QueryTransparency, RadioGroup, Rating, Responsive, RichTextEditor, SearchBar, SearchableList, Select, Separator, Show, Sidebar, SidebarGroup, Skeleton, SkeletonCard$1 as SkeletonCard, SkeletonTable, Slider, Spreadsheet, SpreadsheetReport, Stack, StatCard, StatItem, StatsCardGrid, StatsGrid, StatusBadge, StatusBar, StepIndicator, Stepper, SwipeActions, Switch, Tabs, Text, Textarea, ThemeToggle, TimePicker, Timeline, Toast, ToastContainer, Tooltip, Transfer, TreeView, TwoColumnContent, UserProfileButton, addErrorMessage, addInfoMessage, addSuccessMessage, addWarningMessage, calculateColumnWidth, createActionsSection, createFiltersSection, createMultiSheetExcel, createPageControlsSection, createQueryDetailsSection, exportDataTableToExcel, exportToExcel, formatStatisticValue, formatStatistics, getFormula, getFormulasByCategory, loadColumnOrder, loadColumnWidths, reorderArray, saveColumnOrder, saveColumnWidths, searchFormulas, statusManager, useBreadcrumbReset, useBreakpoint, useBreakpointValue, useColumnReorder, useColumnResize, useCommandPalette, useConfirmDialog, useFABScroll, useFormContext, useIsDesktop, useIsMobile, useIsTablet, useIsTouchDevice, useMediaQuery, useMobileContext, useOrientation, usePrefersMobile, usePullToRefresh, useResponsiveCallback, useSafeAreaInsets, useViewportSize, withMobileContext };
57663
+ export { Accordion, ActionBar, ActionBarCenter, ActionBarLeft, ActionBarRight, ActionButton, 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, Checkbox, CheckboxList, Chip, ChipGroup, 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, KanbanBoard, Layout, Loading, LoadingOverlay, Logo, MarkdownEditor, MaskedInput, Menu, MenuDivider, MobileHeader, MobileHeaderSpacer, MobileLayout, MobileOnly, MobileProvider, Modal, ModalFooter, MultiSelect, NotificationBanner, NotificationBar, NotificationIndicator, NumberInput, Page, PageHeader, PageLayout, PageNavigation, Pagination, PasswordInput, Popover, Progress, PullToRefresh, QueryTransparency, RadioGroup, Rating, Responsive, RichTextEditor, SearchBar, SearchableList, Select, Separator, Show, Sidebar, SidebarGroup, Skeleton, SkeletonCard$1 as SkeletonCard, SkeletonTable, Slider, Spreadsheet, SpreadsheetReport, Stack, StatCard, StatItem, StatsCardGrid, StatsGrid, StatusBadge, StatusBar, StepIndicator, Stepper, SwipeActions, SwipeableCard, Switch, Tabs, Text, Textarea, ThemeToggle, TimePicker, Timeline, Toast, ToastContainer, Tooltip, Transfer, TreeView, TwoColumnContent, UserProfileButton, addErrorMessage, addInfoMessage, addSuccessMessage, addWarningMessage, calculateColumnWidth, createActionsSection, createFiltersSection, createMultiSheetExcel, createPageControlsSection, createQueryDetailsSection, exportDataTableToExcel, exportToExcel, formatStatisticValue, formatStatistics, getFormula, getFormulasByCategory, loadColumnOrder, loadColumnWidths, reorderArray, saveColumnOrder, saveColumnWidths, searchFormulas, statusManager, useBreadcrumbReset, useBreakpoint, useBreakpointValue, useColumnReorder, useColumnResize, useCommandPalette, useConfirmDialog, useFABScroll, useFormContext, useIsDesktop, useIsMobile, useIsTablet, useIsTouchDevice, useMediaQuery, useMobileContext, useOrientation, usePrefersMobile, useResponsiveCallback, useSafeAreaInsets, useViewportSize, withMobileContext };
56985
57664
  //# sourceMappingURL=index.esm.js.map