@papernote/ui 1.9.3 → 1.10.1

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)
@@ -10546,6 +10864,9 @@ function getColumnStyle(column, dynamicWidth) {
10546
10864
  if (column.align) {
10547
10865
  style.textAlign = column.align;
10548
10866
  }
10867
+ if (column.verticalAlign) {
10868
+ style.verticalAlign = column.verticalAlign;
10869
+ }
10549
10870
  return style;
10550
10871
  }
10551
10872
  /**
@@ -57870,5 +58191,5 @@ function Responsive({ mobile, tablet, desktop, }) {
57870
58191
  return jsx(Fragment, { children: mobile || tablet || desktop });
57871
58192
  }
57872
58193
 
57873
- 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 };
58194
+ 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 };
57874
58195
  //# sourceMappingURL=index.esm.js.map