@papernote/ui 1.9.2 → 1.10.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.
package/dist/index.esm.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
2
  import * as React from 'react';
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';
3
+ import React__default, { forwardRef, useState, useEffect, useCallback, useRef, useId, useImperativeHandle, useMemo, Children, isValidElement, cloneElement, Component, createContext as createContext$1, useContext, useLayoutEffect, createElement, useReducer } from 'react';
4
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, ExternalLink, 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';
@@ -8171,11 +8171,254 @@ function Breadcrumbs({ items, showHome = true }) {
8171
8171
  })] }));
8172
8172
  }
8173
8173
 
8174
- function Tabs({ tabs, activeTab: controlledActiveTab, defaultTab, variant = 'underline', orientation = 'horizontal', size = 'md', onChange }) {
8174
+ function Badge({ children, variant = 'neutral', size = 'md', icon, onRemove, className = '', dot = false, pill = false, }) {
8175
+ const variantStyles = {
8176
+ success: 'bg-success-50 text-success-700 border-success-200',
8177
+ warning: 'bg-warning-50 text-warning-700 border-warning-200',
8178
+ error: 'bg-error-50 text-error-700 border-error-200',
8179
+ info: 'bg-primary-50 text-primary-700 border-primary-200',
8180
+ neutral: 'bg-paper-100 text-ink-700 border-paper-300',
8181
+ };
8182
+ const dotVariantStyles = {
8183
+ success: 'bg-success-500',
8184
+ warning: 'bg-warning-500',
8185
+ error: 'bg-error-500',
8186
+ info: 'bg-primary-500',
8187
+ neutral: 'bg-ink-400',
8188
+ };
8189
+ const sizeStyles = {
8190
+ sm: 'px-2 py-0.5 text-xs gap-1',
8191
+ md: 'px-3 py-1 text-xs gap-1.5',
8192
+ lg: 'px-3 py-1.5 text-sm gap-2',
8193
+ };
8194
+ // Pill variant has tighter horizontal padding and fully rounded ends
8195
+ const pillSizeStyles = {
8196
+ sm: 'px-1.5 py-0.5 text-xs gap-1',
8197
+ md: 'px-2 py-0.5 text-xs gap-1',
8198
+ lg: 'px-2.5 py-1 text-sm gap-1.5',
8199
+ };
8200
+ const dotSizeStyles = {
8201
+ sm: 'h-1.5 w-1.5',
8202
+ md: 'h-2 w-2',
8203
+ lg: 'h-2.5 w-2.5',
8204
+ };
8205
+ const iconSize = {
8206
+ sm: 'h-3 w-3',
8207
+ md: 'h-3.5 w-3.5',
8208
+ lg: 'h-4 w-4',
8209
+ };
8210
+ // Dot variant - just a colored circle
8211
+ if (dot) {
8212
+ return (jsx("span", { className: `
8213
+ inline-block rounded-full
8214
+ ${dotVariantStyles[variant]}
8215
+ ${dotSizeStyles[size]}
8216
+ ${className}
8217
+ `, "aria-label": `${variant} indicator` }));
8218
+ }
8219
+ // Regular badge
8220
+ return (jsxs("span", { className: `
8221
+ inline-flex items-center border font-medium
8222
+ ${pill ? 'rounded-full' : 'rounded-full'}
8223
+ ${variantStyles[variant]}
8224
+ ${pill ? pillSizeStyles[size] : sizeStyles[size]}
8225
+ ${className}
8226
+ `, 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] }) }))] }));
8227
+ }
8228
+
8229
+ const TabsContext = createContext$1(null);
8230
+ function useTabsContext() {
8231
+ const context = useContext(TabsContext);
8232
+ if (!context) {
8233
+ throw new Error('Tabs compound components must be used within a TabsRoot component');
8234
+ }
8235
+ return context;
8236
+ }
8237
+ /**
8238
+ * TabsRoot - Root component for compound tabs pattern
8239
+ *
8240
+ * @example
8241
+ * ```tsx
8242
+ * <TabsRoot defaultValue="tab1">
8243
+ * <TabsList>
8244
+ * <TabsTrigger value="tab1">Tab 1</TabsTrigger>
8245
+ * <TabsTrigger value="tab2">Tab 2</TabsTrigger>
8246
+ * </TabsList>
8247
+ * <TabsContent value="tab1">Content 1</TabsContent>
8248
+ * <TabsContent value="tab2">Content 2</TabsContent>
8249
+ * </TabsRoot>
8250
+ * ```
8251
+ */
8252
+ function TabsRoot({ children, defaultValue, value: controlledValue, onValueChange, variant = 'underline', orientation = 'horizontal', size = 'md', lazy = false, preserveState = false, className = '', }) {
8253
+ const [tabValues, setTabValues] = useState([]);
8254
+ const [internalValue, setInternalValue] = useState(defaultValue || '');
8255
+ const [visitedTabs, setVisitedTabs] = useState(new Set(defaultValue ? [defaultValue] : []));
8256
+ const isControlled = controlledValue !== undefined;
8257
+ const activeTab = isControlled ? controlledValue : internalValue;
8258
+ // Set initial value when tabs register
8259
+ useEffect(() => {
8260
+ if (!activeTab && tabValues.length > 0) {
8261
+ const firstTab = tabValues[0];
8262
+ if (isControlled) {
8263
+ onValueChange?.(firstTab);
8264
+ }
8265
+ else {
8266
+ setInternalValue(firstTab);
8267
+ }
8268
+ }
8269
+ }, [tabValues, activeTab, isControlled, onValueChange]);
8270
+ // Track visited tabs
8271
+ useEffect(() => {
8272
+ if (activeTab && preserveState) {
8273
+ setVisitedTabs(prev => new Set(prev).add(activeTab));
8274
+ }
8275
+ }, [activeTab, preserveState]);
8276
+ const setActiveTab = useCallback((value) => {
8277
+ if (!isControlled) {
8278
+ setInternalValue(value);
8279
+ }
8280
+ onValueChange?.(value);
8281
+ }, [isControlled, onValueChange]);
8282
+ const registerTab = useCallback((value) => {
8283
+ setTabValues(prev => prev.includes(value) ? prev : [...prev, value]);
8284
+ }, []);
8285
+ const unregisterTab = useCallback((value) => {
8286
+ setTabValues(prev => prev.filter(v => v !== value));
8287
+ }, []);
8288
+ const contextValue = {
8289
+ activeTab,
8290
+ setActiveTab,
8291
+ variant,
8292
+ orientation,
8293
+ size,
8294
+ lazy,
8295
+ preserveState,
8296
+ visitedTabs,
8297
+ registerTab,
8298
+ unregisterTab,
8299
+ tabValues,
8300
+ };
8301
+ // Size-specific gap classes
8302
+ const gapClasses = {
8303
+ sm: orientation === 'vertical' ? 'gap-1.5' : 'gap-4',
8304
+ md: orientation === 'vertical' ? 'gap-2' : 'gap-6',
8305
+ lg: orientation === 'vertical' ? 'gap-3' : 'gap-8',
8306
+ };
8307
+ return (jsx(TabsContext.Provider, { value: contextValue, children: jsx("div", { className: `w-full ${orientation === 'vertical' ? `flex ${gapClasses[size]}` : ''} ${className}`, children: children }) }));
8308
+ }
8309
+ /**
8310
+ * TabsList - Container for tab triggers
8311
+ */
8312
+ function TabsList({ children, className = '' }) {
8313
+ const { variant, orientation, size } = useTabsContext();
8314
+ const sizeClasses = {
8315
+ sm: {
8316
+ gap: orientation === 'vertical' ? 'gap-1.5' : 'gap-4',
8317
+ minWidth: orientation === 'vertical' ? 'min-w-[150px]' : '',
8318
+ },
8319
+ md: {
8320
+ gap: orientation === 'vertical' ? 'gap-2' : 'gap-6',
8321
+ minWidth: orientation === 'vertical' ? 'min-w-[200px]' : '',
8322
+ },
8323
+ lg: {
8324
+ gap: orientation === 'vertical' ? 'gap-3' : 'gap-8',
8325
+ minWidth: orientation === 'vertical' ? 'min-w-[250px]' : '',
8326
+ },
8327
+ };
8328
+ return (jsx("div", { className: `
8329
+ flex ${orientation === 'vertical' ? 'flex-col' : 'items-center'}
8330
+ ${variant === 'underline'
8331
+ ? orientation === 'vertical'
8332
+ ? `border-r border-paper-200 ${sizeClasses[size].gap} pr-6`
8333
+ : `border-b border-paper-200 ${sizeClasses[size].gap}`
8334
+ : `${sizeClasses[size].gap} p-1 bg-paper-50 rounded-lg`}
8335
+ ${sizeClasses[size].minWidth}
8336
+ ${className}
8337
+ `, role: "tablist", "aria-orientation": orientation, children: children }));
8338
+ }
8339
+ /**
8340
+ * TabsTrigger - Individual tab button
8341
+ */
8342
+ function TabsTrigger({ children, value, disabled = false, icon, badge, badgeVariant = 'info', className = '', }) {
8343
+ const { activeTab, setActiveTab, variant, orientation, size, registerTab, unregisterTab } = useTabsContext();
8344
+ const isActive = activeTab === value;
8345
+ // Register this tab on mount
8346
+ useEffect(() => {
8347
+ registerTab(value);
8348
+ return () => unregisterTab(value);
8349
+ }, [value, registerTab, unregisterTab]);
8350
+ const sizeClasses = {
8351
+ sm: { padding: 'px-3 py-1.5', text: 'text-xs', icon: 'h-3.5 w-3.5', badgeSize: 'sm' },
8352
+ md: { padding: 'px-4 py-2.5', text: 'text-sm', icon: 'h-4 w-4', badgeSize: 'sm' },
8353
+ lg: { padding: 'px-5 py-3', text: 'text-base', icon: 'h-5 w-5', badgeSize: 'md' },
8354
+ };
8355
+ return (jsxs("button", { role: "tab", "aria-selected": isActive, "aria-controls": `panel-${value}`, "aria-disabled": disabled, tabIndex: isActive ? 0 : -1, disabled: disabled, onClick: () => !disabled && setActiveTab(value), className: `
8356
+ flex items-center gap-2 ${sizeClasses[size].padding} ${sizeClasses[size].text} font-medium transition-all duration-200
8357
+ ${orientation === 'vertical' ? 'w-full justify-start' : ''}
8358
+ ${variant === 'underline'
8359
+ ? isActive
8360
+ ? orientation === 'vertical'
8361
+ ? 'text-accent-900 border-r-2 border-accent-500 -mr-[1px]'
8362
+ : 'text-accent-900 border-b-2 border-accent-500 -mb-[1px]'
8363
+ : orientation === 'vertical'
8364
+ ? 'text-ink-600 hover:text-ink-900 border-r-2 border-transparent'
8365
+ : 'text-ink-600 hover:text-ink-900 border-b-2 border-transparent'
8366
+ : isActive
8367
+ ? 'bg-white text-accent-900 rounded-md shadow-xs'
8368
+ : 'text-ink-600 hover:text-ink-900 hover:bg-white/50 rounded-md'}
8369
+ ${disabled ? 'opacity-40 cursor-not-allowed' : 'cursor-pointer'}
8370
+ focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-500 focus-visible:ring-offset-1
8371
+ ${className}
8372
+ `, children: [icon && jsx("span", { className: `flex-shrink-0 ${sizeClasses[size].icon}`, children: icon }), jsx("span", { children: children }), badge !== undefined && (jsx(Badge, { variant: badgeVariant, size: sizeClasses[size].badgeSize, children: badge }))] }));
8373
+ }
8374
+ /**
8375
+ * TabsContent - Content panel for a tab
8376
+ */
8377
+ function TabsContent({ children, value, className = '' }) {
8378
+ const { activeTab, lazy, preserveState, visitedTabs, orientation, size } = useTabsContext();
8379
+ const isActive = activeTab === value;
8380
+ // Determine if content should be rendered
8381
+ const shouldRender = !lazy || isActive || (preserveState && visitedTabs.has(value));
8382
+ if (!shouldRender) {
8383
+ return null;
8384
+ }
8385
+ const spacingClasses = {
8386
+ sm: orientation === 'vertical' ? '' : 'mt-4',
8387
+ md: orientation === 'vertical' ? '' : 'mt-6',
8388
+ lg: orientation === 'vertical' ? '' : 'mt-8',
8389
+ };
8390
+ return (jsx("div", { id: `panel-${value}`, role: "tabpanel", "aria-labelledby": `tab-${value}`, hidden: !isActive, tabIndex: 0, className: `
8391
+ ${orientation === 'vertical' ? 'flex-1' : spacingClasses[size]}
8392
+ ${isActive ? 'animate-fade-in focus:outline-none' : 'hidden'}
8393
+ ${className}
8394
+ `, children: children }));
8395
+ }
8396
+ // =============================================================================
8397
+ // Data-Driven Component (Original API)
8398
+ // =============================================================================
8399
+ /**
8400
+ * Tabs - Data-driven tabs component
8401
+ *
8402
+ * Use this for simple use cases where tabs are defined as an array.
8403
+ * For more complex layouts, use the compound components (TabsRoot, TabsList, TabsTrigger, TabsContent).
8404
+ */
8405
+ function Tabs({ tabs, activeTab: controlledActiveTab, defaultTab, variant = 'underline', orientation = 'horizontal', size = 'md', onChange, lazy = false, preserveState = false, closeable = false, onClose, showAddButton = false, onAdd, addButtonLabel = 'Add tab', }) {
8175
8406
  const [internalActiveTab, setInternalActiveTab] = useState(defaultTab || tabs[0]?.id);
8407
+ const [focusedIndex, setFocusedIndex] = useState(-1);
8408
+ const [visitedTabs, setVisitedTabs] = useState(new Set([defaultTab || tabs[0]?.id]));
8409
+ const tabListRef = useRef(null);
8410
+ const tabRefs = useRef([]);
8176
8411
  // Controlled mode: use activeTab prop, Uncontrolled mode: use internal state
8177
8412
  const isControlled = controlledActiveTab !== undefined;
8178
8413
  const activeTab = isControlled ? controlledActiveTab : internalActiveTab;
8414
+ // Track visited tabs for preserveState
8415
+ useEffect(() => {
8416
+ if (activeTab && preserveState) {
8417
+ setVisitedTabs(prev => new Set(prev).add(activeTab));
8418
+ }
8419
+ }, [activeTab, preserveState]);
8420
+ // Get enabled tab indices for keyboard navigation
8421
+ const enabledIndices = tabs.map((tab, index) => tab.disabled ? -1 : index).filter(i => i !== -1);
8179
8422
  // Ensure the activeTab exists in the current tabs array
8180
8423
  // This handles the case where tabs array reference changes at the same time as activeTab
8181
8424
  useEffect(() => {
@@ -8190,66 +8433,196 @@ function Tabs({ tabs, activeTab: controlledActiveTab, defaultTab, variant = 'und
8190
8433
  }
8191
8434
  }
8192
8435
  }, [tabs, activeTab, isControlled, onChange]);
8193
- const handleTabChange = (tabId) => {
8436
+ const handleTabChange = useCallback((tabId) => {
8194
8437
  if (!isControlled) {
8195
8438
  setInternalActiveTab(tabId);
8196
8439
  }
8197
8440
  onChange?.(tabId);
8198
- };
8441
+ }, [isControlled, onChange]);
8442
+ // Handle tab close
8443
+ const handleClose = useCallback((e, tabId) => {
8444
+ e.stopPropagation();
8445
+ onClose?.(tabId);
8446
+ }, [onClose]);
8447
+ // Keyboard navigation handler
8448
+ const handleKeyDown = useCallback((e) => {
8449
+ const currentIndex = focusedIndex >= 0 ? focusedIndex : tabs.findIndex(t => t.id === activeTab);
8450
+ const currentEnabledPosition = enabledIndices.indexOf(currentIndex);
8451
+ let nextIndex = -1;
8452
+ let shouldPreventDefault = true;
8453
+ // Determine navigation keys based on orientation
8454
+ const prevKey = orientation === 'vertical' ? 'ArrowUp' : 'ArrowLeft';
8455
+ const nextKey = orientation === 'vertical' ? 'ArrowDown' : 'ArrowRight';
8456
+ switch (e.key) {
8457
+ case prevKey:
8458
+ // Move to previous enabled tab
8459
+ if (currentEnabledPosition > 0) {
8460
+ nextIndex = enabledIndices[currentEnabledPosition - 1];
8461
+ }
8462
+ else {
8463
+ // Wrap to last enabled tab
8464
+ nextIndex = enabledIndices[enabledIndices.length - 1];
8465
+ }
8466
+ break;
8467
+ case nextKey:
8468
+ // Move to next enabled tab
8469
+ if (currentEnabledPosition < enabledIndices.length - 1) {
8470
+ nextIndex = enabledIndices[currentEnabledPosition + 1];
8471
+ }
8472
+ else {
8473
+ // Wrap to first enabled tab
8474
+ nextIndex = enabledIndices[0];
8475
+ }
8476
+ break;
8477
+ case 'Home':
8478
+ // Move to first enabled tab
8479
+ nextIndex = enabledIndices[0];
8480
+ break;
8481
+ case 'End':
8482
+ // Move to last enabled tab
8483
+ nextIndex = enabledIndices[enabledIndices.length - 1];
8484
+ break;
8485
+ case 'Enter':
8486
+ case ' ':
8487
+ // Activate focused tab
8488
+ if (focusedIndex >= 0 && !tabs[focusedIndex]?.disabled) {
8489
+ handleTabChange(tabs[focusedIndex].id);
8490
+ }
8491
+ break;
8492
+ case 'Delete':
8493
+ case 'Backspace':
8494
+ // Close focused tab if closeable
8495
+ if (focusedIndex >= 0) {
8496
+ const tab = tabs[focusedIndex];
8497
+ const isTabCloseable = tab.closeable !== undefined ? tab.closeable : closeable;
8498
+ if (isTabCloseable && !tab.disabled && onClose) {
8499
+ onClose(tab.id);
8500
+ }
8501
+ }
8502
+ break;
8503
+ default:
8504
+ shouldPreventDefault = false;
8505
+ }
8506
+ if (shouldPreventDefault) {
8507
+ e.preventDefault();
8508
+ }
8509
+ if (nextIndex >= 0 && nextIndex !== currentIndex) {
8510
+ setFocusedIndex(nextIndex);
8511
+ tabRefs.current[nextIndex]?.focus();
8512
+ }
8513
+ }, [focusedIndex, tabs, activeTab, enabledIndices, orientation, handleTabChange, closeable, onClose]);
8514
+ // Handle focus events
8515
+ const handleFocus = useCallback((index) => {
8516
+ setFocusedIndex(index);
8517
+ }, []);
8518
+ const handleBlur = useCallback(() => {
8519
+ setFocusedIndex(-1);
8520
+ }, []);
8199
8521
  // Size-specific classes
8200
8522
  const sizeClasses = {
8201
8523
  sm: {
8202
8524
  padding: 'px-3 py-1.5',
8203
8525
  text: 'text-xs',
8204
8526
  icon: 'h-3.5 w-3.5',
8527
+ closeIcon: 'h-3 w-3',
8205
8528
  gap: orientation === 'vertical' ? 'gap-1.5' : 'gap-4',
8206
8529
  minWidth: orientation === 'vertical' ? 'min-w-[150px]' : '',
8207
8530
  spacing: orientation === 'vertical' ? 'mt-4' : 'mt-4',
8531
+ badgeSize: 'sm',
8532
+ addPadding: 'px-2 py-1.5',
8208
8533
  },
8209
8534
  md: {
8210
8535
  padding: 'px-4 py-2.5',
8211
8536
  text: 'text-sm',
8212
8537
  icon: 'h-4 w-4',
8538
+ closeIcon: 'h-3.5 w-3.5',
8213
8539
  gap: orientation === 'vertical' ? 'gap-2' : 'gap-6',
8214
8540
  minWidth: orientation === 'vertical' ? 'min-w-[200px]' : '',
8215
8541
  spacing: orientation === 'vertical' ? 'mt-6' : 'mt-6',
8542
+ badgeSize: 'sm',
8543
+ addPadding: 'px-3 py-2.5',
8216
8544
  },
8217
8545
  lg: {
8218
8546
  padding: 'px-5 py-3',
8219
8547
  text: 'text-base',
8220
8548
  icon: 'h-5 w-5',
8549
+ closeIcon: 'h-4 w-4',
8221
8550
  gap: orientation === 'vertical' ? 'gap-3' : 'gap-8',
8222
8551
  minWidth: orientation === 'vertical' ? 'min-w-[250px]' : '',
8223
8552
  spacing: orientation === 'vertical' ? 'mt-8' : 'mt-8',
8553
+ badgeSize: 'md',
8554
+ addPadding: 'px-4 py-3',
8224
8555
  },
8225
8556
  };
8226
- return (jsxs("div", { className: `w-full ${orientation === 'vertical' ? `flex ${sizeClasses[size].gap}` : ''}`, children: [jsx("div", { className: `
8227
- flex ${orientation === 'vertical' ? 'flex-col' : ''}
8557
+ // Determine which tabs should be rendered
8558
+ const shouldRenderContent = useCallback((tabId) => {
8559
+ if (!lazy) {
8560
+ // Not lazy: render all tabs
8561
+ return true;
8562
+ }
8563
+ if (tabId === activeTab) {
8564
+ // Always render active tab
8565
+ return true;
8566
+ }
8567
+ if (preserveState && visitedTabs.has(tabId)) {
8568
+ // Preserve state: render previously visited tabs
8569
+ return true;
8570
+ }
8571
+ return false;
8572
+ }, [lazy, activeTab, preserveState, visitedTabs]);
8573
+ return (jsxs("div", { className: `w-full ${orientation === 'vertical' ? `flex ${sizeClasses[size].gap}` : ''}`, children: [jsxs("div", { ref: tabListRef, className: `
8574
+ flex ${orientation === 'vertical' ? 'flex-col' : 'items-center'}
8228
8575
  ${variant === 'underline'
8229
8576
  ? orientation === 'vertical'
8230
8577
  ? `border-r border-paper-200 ${sizeClasses[size].gap} pr-6`
8231
8578
  : `border-b border-paper-200 ${sizeClasses[size].gap}`
8232
8579
  : `${sizeClasses[size].gap} p-1 bg-paper-50 rounded-lg`}
8233
8580
  ${sizeClasses[size].minWidth}
8234
- `, role: "tablist", children: tabs.map((tab) => {
8235
- const isActive = activeTab === tab.id;
8236
- return (jsxs("button", { role: "tab", "aria-selected": isActive, "aria-controls": `panel-${tab.id}`, disabled: tab.disabled, onClick: () => !tab.disabled && handleTabChange(tab.id), className: `
8581
+ `, role: "tablist", "aria-orientation": orientation, onKeyDown: handleKeyDown, children: [tabs.map((tab, index) => {
8582
+ const isActive = activeTab === tab.id;
8583
+ const isTabCloseable = tab.closeable !== undefined ? tab.closeable : closeable;
8584
+ return (jsxs("button", { ref: (el) => { tabRefs.current[index] = el; }, id: `tab-${tab.id}`, role: "tab", "aria-selected": isActive, "aria-controls": `panel-${tab.id}`, "aria-disabled": tab.disabled, tabIndex: isActive ? 0 : -1, disabled: tab.disabled, onClick: () => !tab.disabled && handleTabChange(tab.id), onFocus: () => handleFocus(index), onBlur: handleBlur, className: `
8237
8585
  flex items-center gap-2 ${sizeClasses[size].padding} ${sizeClasses[size].text} font-medium transition-all duration-200
8238
8586
  ${orientation === 'vertical' ? 'w-full justify-start' : ''}
8239
8587
  ${variant === 'underline'
8240
- ? isActive
8241
- ? orientation === 'vertical'
8242
- ? 'text-accent-900 border-r-2 border-accent-500 -mr-[1px]'
8243
- : 'text-accent-900 border-b-2 border-accent-500 -mb-[1px]'
8244
- : orientation === 'vertical'
8245
- ? 'text-ink-600 hover:text-ink-900 border-r-2 border-transparent'
8246
- : 'text-ink-600 hover:text-ink-900 border-b-2 border-transparent'
8247
- : isActive
8248
- ? 'bg-white text-accent-900 rounded-md shadow-xs'
8249
- : 'text-ink-600 hover:text-ink-900 hover:bg-white/50 rounded-md'}
8588
+ ? isActive
8589
+ ? orientation === 'vertical'
8590
+ ? 'text-accent-900 border-r-2 border-accent-500 -mr-[1px]'
8591
+ : 'text-accent-900 border-b-2 border-accent-500 -mb-[1px]'
8592
+ : orientation === 'vertical'
8593
+ ? 'text-ink-600 hover:text-ink-900 border-r-2 border-transparent'
8594
+ : 'text-ink-600 hover:text-ink-900 border-b-2 border-transparent'
8595
+ : isActive
8596
+ ? 'bg-white text-accent-900 rounded-md shadow-xs'
8597
+ : 'text-ink-600 hover:text-ink-900 hover:bg-white/50 rounded-md'}
8250
8598
  ${tab.disabled ? 'opacity-40 cursor-not-allowed' : 'cursor-pointer'}
8251
- `, children: [tab.icon && jsx("span", { className: `flex-shrink-0 ${sizeClasses[size].icon}`, children: tab.icon }), jsx("span", { children: tab.label })] }, tab.id));
8252
- }) }), jsx("div", { className: `${orientation === 'vertical' ? 'flex-1' : sizeClasses[size].spacing}`, children: tabs.map((tab) => (jsx("div", { id: `panel-${tab.id}`, role: "tabpanel", "aria-labelledby": tab.id, hidden: activeTab !== tab.id, className: activeTab === tab.id ? 'animate-fade-in' : '', children: activeTab === tab.id && tab.content }, tab.id))) })] }));
8599
+ focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-500 focus-visible:ring-offset-1
8600
+ group
8601
+ `, children: [tab.icon && jsx("span", { className: `flex-shrink-0 ${sizeClasses[size].icon}`, children: tab.icon }), jsx("span", { className: isTabCloseable ? 'mr-1' : '', children: tab.label }), tab.badge !== undefined && (jsx(Badge, { variant: tab.badgeVariant || 'info', size: sizeClasses[size].badgeSize, children: tab.badge })), isTabCloseable && onClose && (jsx("span", { role: "button", "aria-label": `Close ${tab.label}`, onClick: (e) => handleClose(e, tab.id), className: `
8602
+ flex-shrink-0 p-0.5 rounded
8603
+ text-ink-400 hover:text-ink-700 hover:bg-paper-200
8604
+ opacity-0 group-hover:opacity-100 group-focus-visible:opacity-100
8605
+ ${isActive ? 'opacity-100' : ''}
8606
+ transition-opacity duration-150
8607
+ `, children: jsx(X, { className: sizeClasses[size].closeIcon }) }))] }, tab.id));
8608
+ }), showAddButton && onAdd && (jsx("button", { type: "button", onClick: onAdd, "aria-label": addButtonLabel, title: addButtonLabel, className: `
8609
+ flex items-center justify-center ${sizeClasses[size].addPadding}
8610
+ text-ink-500 hover:text-ink-700 hover:bg-paper-100
8611
+ rounded transition-colors duration-150
8612
+ focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-500 focus-visible:ring-offset-1
8613
+ ${variant === 'underline'
8614
+ ? orientation === 'vertical'
8615
+ ? 'border-r-2 border-transparent'
8616
+ : 'border-b-2 border-transparent -mb-[1px]'
8617
+ : ''}
8618
+ `, children: jsx(Plus, { className: sizeClasses[size].icon }) }))] }), jsx("div", { className: `${orientation === 'vertical' ? 'flex-1' : sizeClasses[size].spacing}`, children: tabs.map((tab) => {
8619
+ const isActive = activeTab === tab.id;
8620
+ const shouldRender = shouldRenderContent(tab.id);
8621
+ if (!shouldRender) {
8622
+ return null;
8623
+ }
8624
+ return (jsx("div", { id: `panel-${tab.id}`, role: "tabpanel", "aria-labelledby": `tab-${tab.id}`, hidden: !isActive, tabIndex: 0, className: isActive ? 'animate-fade-in focus:outline-none' : 'hidden', children: tab.content }, tab.id));
8625
+ }) })] }));
8253
8626
  }
8254
8627
 
8255
8628
  function Pagination({ currentPage, totalPages, onPageChange, showPageNumbers = true, maxPageNumbers = 5, showPageJump = false, }) {
@@ -8323,61 +8696,6 @@ function StepIndicator({ steps, currentStep, variant = 'horizontal', onStepClick
8323
8696
  }) }) }));
8324
8697
  }
8325
8698
 
8326
- function Badge({ children, variant = 'neutral', size = 'md', icon, onRemove, className = '', dot = false, pill = false, }) {
8327
- const variantStyles = {
8328
- success: 'bg-success-50 text-success-700 border-success-200',
8329
- warning: 'bg-warning-50 text-warning-700 border-warning-200',
8330
- error: 'bg-error-50 text-error-700 border-error-200',
8331
- info: 'bg-primary-50 text-primary-700 border-primary-200',
8332
- neutral: 'bg-paper-100 text-ink-700 border-paper-300',
8333
- };
8334
- const dotVariantStyles = {
8335
- success: 'bg-success-500',
8336
- warning: 'bg-warning-500',
8337
- error: 'bg-error-500',
8338
- info: 'bg-primary-500',
8339
- neutral: 'bg-ink-400',
8340
- };
8341
- const sizeStyles = {
8342
- sm: 'px-2 py-0.5 text-xs gap-1',
8343
- md: 'px-3 py-1 text-xs gap-1.5',
8344
- lg: 'px-3 py-1.5 text-sm gap-2',
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
- };
8352
- const dotSizeStyles = {
8353
- sm: 'h-1.5 w-1.5',
8354
- md: 'h-2 w-2',
8355
- lg: 'h-2.5 w-2.5',
8356
- };
8357
- const iconSize = {
8358
- sm: 'h-3 w-3',
8359
- md: 'h-3.5 w-3.5',
8360
- lg: 'h-4 w-4',
8361
- };
8362
- // Dot variant - just a colored circle
8363
- if (dot) {
8364
- return (jsx("span", { className: `
8365
- inline-block rounded-full
8366
- ${dotVariantStyles[variant]}
8367
- ${dotSizeStyles[size]}
8368
- ${className}
8369
- `, "aria-label": `${variant} indicator` }));
8370
- }
8371
- // Regular badge
8372
- return (jsxs("span", { className: `
8373
- inline-flex items-center border font-medium
8374
- ${pill ? 'rounded-full' : 'rounded-full'}
8375
- ${variantStyles[variant]}
8376
- ${pill ? pillSizeStyles[size] : sizeStyles[size]}
8377
- ${className}
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] }) }))] }));
8379
- }
8380
-
8381
8699
  /**
8382
8700
  * Avatar component that displays:
8383
8701
  * - User initials in a colored circle (default)
@@ -10152,7 +10470,7 @@ function getPopoverPlacement(position) {
10152
10470
  * />
10153
10471
  * ```
10154
10472
  */
10155
- function NotificationBell({ notifications, unreadCount: providedUnreadCount, onMarkAsRead, onMarkAllRead, onNotificationClick, onViewAll, loading = false, dropdownPosition = 'bottom-right', maxHeight = '400px', size = 'md', emptyMessage = 'No notifications', viewAllText = 'View all notifications', disabled = false, className = '', variant = 'compact', showUnreadInHeader = false, bellStyle = 'ghost', }) {
10473
+ function NotificationBell({ notifications, unreadCount: providedUnreadCount, onMarkAsRead, onMarkAllRead, onNotificationClick, onViewAll, loading = false, dropdownPosition = 'bottom-right', maxHeight = '400px', size = 'md', emptyMessage = 'No notifications', viewAllText = 'View all notifications', disabled = false, className = '', variant = 'compact', showUnreadInHeader = false, bellStyle = 'ghost', icon: customIcon, }) {
10156
10474
  const [isOpen, setIsOpen] = useState(false);
10157
10475
  // Calculate unread count if not provided
10158
10476
  const unreadCount = useMemo(() => {
@@ -10197,6 +10515,8 @@ function NotificationBell({ notifications, unreadCount: providedUnreadCount, onM
10197
10515
  md: 'p-3',
10198
10516
  lg: 'p-4',
10199
10517
  };
10518
+ // Default bell icon or custom icon
10519
+ const bellIcon = customIcon || jsx(Bell, { className: `${iconSizes[size]} text-ink-600` });
10200
10520
  // Trigger button
10201
10521
  const triggerButton = bellStyle === 'outlined' ? (jsxs("div", { className: "relative inline-block", children: [jsx("button", { className: `
10202
10522
  ${outlinedSizeClasses[size]}
@@ -10208,16 +10528,16 @@ function NotificationBell({ notifications, unreadCount: providedUnreadCount, onM
10208
10528
  ${className}
10209
10529
  `, disabled: disabled, "aria-label": unreadCount > 0
10210
10530
  ? `Notifications - ${unreadCount} unread`
10211
- : 'Notifications', children: jsx(Bell, { className: `${iconSizes[size]} text-ink-600` }) }), unreadCount > 0 && (jsx("span", { className: `
10531
+ : 'Notifications', children: bellIcon }), unreadCount > 0 && (jsx("span", { className: `
10212
10532
  absolute -top-1 -right-1
10213
10533
  flex items-center justify-center
10214
10534
  min-w-[18px] h-[18px] px-1.5
10215
10535
  rounded-full text-white font-semibold text-[11px]
10216
10536
  bg-error-500 shadow-sm
10217
10537
  pointer-events-none
10218
- `, "aria-label": `${unreadCount > 99 ? '99+' : unreadCount} notifications`, children: unreadCount > 99 ? '99+' : unreadCount }))] })) : (jsx(Button, { variant: "ghost", iconOnly: true, size: size, disabled: disabled, badge: unreadCount > 0 ? unreadCount : undefined, badgeVariant: "error", "aria-label": unreadCount > 0
10538
+ `, "aria-label": `${unreadCount > 99 ? '99+' : unreadCount} notifications`, children: unreadCount > 99 ? '99+' : unreadCount }))] })) : (jsx(Button, { variant: "ghost", iconOnly: true, size: size, disabled: disabled, icon: bellIcon, badge: unreadCount > 0 ? unreadCount : undefined, badgeVariant: "error", "aria-label": unreadCount > 0
10219
10539
  ? `Notifications - ${unreadCount} unread`
10220
- : 'Notifications', className: className, children: jsx(Bell, { className: iconSizes[size] }) }));
10540
+ : 'Notifications', className: className }));
10221
10541
  // Header title with optional unread count
10222
10542
  const headerTitle = showUnreadInHeader && unreadCount > 0
10223
10543
  ? `Notifications (${unreadCount} unread)`
@@ -11273,52 +11593,44 @@ function getAugmentedNamespace(n) {
11273
11593
  * (A1, A1:C5, ...)
11274
11594
  */
11275
11595
 
11276
- var collection;
11277
- var hasRequiredCollection;
11278
-
11279
- function requireCollection () {
11280
- if (hasRequiredCollection) return collection;
11281
- hasRequiredCollection = 1;
11282
- class Collection {
11596
+ let Collection$3 = class Collection {
11283
11597
 
11284
- constructor(data, refs) {
11285
- if (data == null && refs == null) {
11286
- this._data = [];
11287
- this._refs = [];
11288
- } else {
11289
- if (data.length !== refs.length)
11290
- throw Error('Collection: data length should match references length.');
11291
- this._data = data;
11292
- this._refs = refs;
11293
- }
11294
- }
11598
+ constructor(data, refs) {
11599
+ if (data == null && refs == null) {
11600
+ this._data = [];
11601
+ this._refs = [];
11602
+ } else {
11603
+ if (data.length !== refs.length)
11604
+ throw Error('Collection: data length should match references length.');
11605
+ this._data = data;
11606
+ this._refs = refs;
11607
+ }
11608
+ }
11295
11609
 
11296
- get data() {
11297
- return this._data;
11298
- }
11610
+ get data() {
11611
+ return this._data;
11612
+ }
11299
11613
 
11300
- get refs() {
11301
- return this._refs;
11302
- }
11614
+ get refs() {
11615
+ return this._refs;
11616
+ }
11303
11617
 
11304
- get length() {
11305
- return this._data.length;
11306
- }
11618
+ get length() {
11619
+ return this._data.length;
11620
+ }
11307
11621
 
11308
- /**
11309
- * Add data and references to this collection.
11310
- * @param {{}} obj - data
11311
- * @param {{}} ref - reference
11312
- */
11313
- add(obj, ref) {
11314
- this._data.push(obj);
11315
- this._refs.push(ref);
11316
- }
11317
- }
11622
+ /**
11623
+ * Add data and references to this collection.
11624
+ * @param {{}} obj - data
11625
+ * @param {{}} ref - reference
11626
+ */
11627
+ add(obj, ref) {
11628
+ this._data.push(obj);
11629
+ this._refs.push(ref);
11630
+ }
11631
+ };
11318
11632
 
11319
- collection = Collection;
11320
- return collection;
11321
- }
11633
+ var collection = Collection$3;
11322
11634
 
11323
11635
  var helpers;
11324
11636
  var hasRequiredHelpers;
@@ -11327,7 +11639,7 @@ function requireHelpers () {
11327
11639
  if (hasRequiredHelpers) return helpers;
11328
11640
  hasRequiredHelpers = 1;
11329
11641
  const FormulaError = requireError();
11330
- const Collection = requireCollection();
11642
+ const Collection = collection;
11331
11643
 
11332
11644
  const Types = {
11333
11645
  NUMBER: 0,
@@ -20981,7 +21293,7 @@ var engineering = EngineeringFunctions;
20981
21293
 
20982
21294
  const FormulaError$b = requireError();
20983
21295
  const {FormulaHelpers: FormulaHelpers$8, Types: Types$6, WildCard, Address: Address$3} = requireHelpers();
20984
- const Collection$2 = requireCollection();
21296
+ const Collection$2 = collection;
20985
21297
  const H$5 = FormulaHelpers$8;
20986
21298
 
20987
21299
  const ReferenceFunctions$1 = {
@@ -32609,7 +32921,7 @@ var parsing = {
32609
32921
  const FormulaError$4 = requireError();
32610
32922
  const {Address: Address$1} = requireHelpers();
32611
32923
  const {Prefix: Prefix$1, Postfix: Postfix$1, Infix: Infix$1, Operators: Operators$1} = operators;
32612
- const Collection$1 = requireCollection();
32924
+ const Collection$1 = collection;
32613
32925
  const MAX_ROW$1 = 1048576, MAX_COLUMN$1 = 16384;
32614
32926
  const {NotAllInputParsedException} = require$$4;
32615
32927
 
@@ -33371,7 +33683,7 @@ var hooks$1 = {
33371
33683
  const FormulaError$2 = requireError();
33372
33684
  const {FormulaHelpers: FormulaHelpers$1, Types, Address} = requireHelpers();
33373
33685
  const {Prefix, Postfix, Infix, Operators} = operators;
33374
- const Collection = requireCollection();
33686
+ const Collection = collection;
33375
33687
  const MAX_ROW = 1048576, MAX_COLUMN = 16384;
33376
33688
 
33377
33689
  let Utils$1 = class Utils {
@@ -57876,5 +58188,5 @@ function Responsive({ mobile, tablet, desktop, }) {
57876
58188
  return jsx(Fragment, { children: mobile || tablet || desktop });
57877
58189
  }
57878
58190
 
57879
- 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, NotificationBell, 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 };
58191
+ 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, NotificationBell, 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, TabsContent, TabsList, TabsRoot, TabsTrigger, 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 };
57880
58192
  //# sourceMappingURL=index.esm.js.map