@metropolle/design-system 1.2025.1-2.5.1903 → 1.2026.0-1.2.1231

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.
@@ -1,4 +1,4 @@
1
- import require$$0, { forwardRef, useState, useEffect, useMemo, useRef } from 'react';
1
+ import require$$0, { forwardRef, useState, useRef, useEffect, useMemo, useCallback } from 'react';
2
2
  import { createPortal } from 'react-dom';
3
3
 
4
4
  var jsxRuntime = {exports: {}};
@@ -1370,12 +1370,35 @@ function cn(...classes) {
1370
1370
  /**
1371
1371
  * Glass Card Component
1372
1372
  *
1373
- * Componente de cartão com efeito glassmorphism baseado na identidade Metropolle.
1374
- * Migrado dos componentes Angular existentes com melhorias.
1373
+ * Componente de cartão com efeito glassmorphism ou Liquid Glass (iOS 26 style).
1374
+ *
1375
+ * @example
1376
+ * ```tsx
1377
+ * // Liquid Glass (novo padrão)
1378
+ * <GlassCard glassStyle="liquid" intensity="md">
1379
+ * Content here
1380
+ * </GlassCard>
1381
+ *
1382
+ * // Glassmorphism tradicional (retrocompatível)
1383
+ * <GlassCard glassStyle="glass" variant="dark">
1384
+ * Content here
1385
+ * </GlassCard>
1386
+ * ```
1375
1387
  */
1376
- const GlassCard = forwardRef(({ variant = 'light', blur = 20, opacity, children, className, enableHover = true, style, ...props }, ref) => {
1388
+ const GlassCard = forwardRef(({ glassStyle = 'liquid', intensity = 'md', theme, variant = 'light', blur, opacity, children, className, enableHover = true, style, ...props }, ref) => {
1389
+ // Resolve theme from new prop or deprecated variant
1390
+ const resolvedTheme = theme ?? variant;
1391
+ // =====================
1392
+ // LIQUID GLASS MODE
1393
+ // =====================
1394
+ if (glassStyle === 'liquid') {
1395
+ return (jsxRuntimeExports.jsx("div", { ref: ref, className: cn('mds-liquid-glass', `mds-liquid-glass--${intensity}`, !enableHover && 'mds-liquid-glass--no-hover', className), style: style, ...props, children: children }));
1396
+ }
1397
+ // =====================
1398
+ // GLASS MODE (legacy)
1399
+ // =====================
1377
1400
  // Default opacities baseadas no design existente
1378
- const defaultOpacity = variant === 'light' ? 0.15 : 0.8;
1401
+ const defaultOpacity = resolvedTheme === 'light' ? 0.15 : 0.8;
1379
1402
  const finalOpacity = opacity ?? defaultOpacity;
1380
1403
  // Use CSS classes for base styles to avoid hydration mismatches
1381
1404
  const baseStyles = {
@@ -1383,12 +1406,16 @@ const GlassCard = forwardRef(({ variant = 'light', blur = 20, opacity, children,
1383
1406
  };
1384
1407
  // Only apply custom styles for non-default values
1385
1408
  const customStyles = {};
1386
- if (blur !== 20) {
1387
- customStyles.backdropFilter = `blur(${blur}px)`;
1388
- customStyles.WebkitBackdropFilter = `blur(${blur}px)`;
1409
+ // Support legacy blur prop or convert from intensity
1410
+ const resolvedBlur = blur ?? (intensity ? {
1411
+ xs: 2, sm: 4, md: 6, lg: 8, xl: 12
1412
+ }[intensity] : 20);
1413
+ if (resolvedBlur !== 20) {
1414
+ customStyles.backdropFilter = `blur(${resolvedBlur}px)`;
1415
+ customStyles.WebkitBackdropFilter = `blur(${resolvedBlur}px)`;
1389
1416
  }
1390
1417
  if (opacity !== undefined) {
1391
- customStyles.background = variant === 'light'
1418
+ customStyles.background = resolvedTheme === 'light'
1392
1419
  ? `rgba(255, 255, 255, ${finalOpacity})`
1393
1420
  : `rgba(60, 60, 60, ${finalOpacity})`;
1394
1421
  }
@@ -1403,7 +1430,7 @@ const GlassCard = forwardRef(({ variant = 'light', blur = 20, opacity, children,
1403
1430
  borderColor: 'rgba(255, 255, 255, 0.2)',
1404
1431
  background: opacity !== undefined ? `rgba(70, 70, 70, ${finalOpacity})` : undefined,
1405
1432
  }
1406
- }[variant] : {};
1433
+ }[resolvedTheme] : {};
1407
1434
  const handleMouseEnter = (e) => {
1408
1435
  if (!enableHover)
1409
1436
  return;
@@ -1421,7 +1448,7 @@ const GlassCard = forwardRef(({ variant = 'light', blur = 20, opacity, children,
1421
1448
  });
1422
1449
  props.onMouseLeave?.(e);
1423
1450
  };
1424
- return (jsxRuntimeExports.jsx("div", { ref: ref, className: cn('mds-glass-card', `mds-glass-card--${variant}`, !enableHover && 'mds-glass-card--no-hover', className), style: {
1451
+ return (jsxRuntimeExports.jsx("div", { ref: ref, className: cn('mds-glass-card', `mds-glass-card--${resolvedTheme}`, !enableHover && 'mds-glass-card--no-hover', className), style: {
1425
1452
  ...baseStyles,
1426
1453
  ...customStyles,
1427
1454
  ...style
@@ -1493,22 +1520,241 @@ const LoadingSpinner = () => (jsxRuntimeExports.jsxs("svg", { className: "mds-sp
1493
1520
  /**
1494
1521
  * Select Component (Design System)
1495
1522
  *
1496
- * Provides a themed select element with multiple variants:
1497
- * - `base`: Standard form select with mds-input styling
1498
- * - `themed`: Generic themed select with dashboard control styling (recommended)
1499
- * - `dashboard`: Legacy alias for themed variant (backward compatibility)
1523
+ * Custom dropdown select that renders consistently across all browsers.
1524
+ * Unlike native <select>, this component renders the dropdown via JavaScript,
1525
+ * ensuring proper theming support on Edge/Chrome Windows.
1526
+ *
1527
+ * @example
1528
+ * ```tsx
1529
+ * <Select
1530
+ * options={[
1531
+ * { label: 'Option 1', value: '1' },
1532
+ * { label: 'Option 2', value: '2' },
1533
+ * ]}
1534
+ * value={selectedValue}
1535
+ * onChange={setSelectedValue}
1536
+ * placeholder="Select an option..."
1537
+ * />
1538
+ * ```
1500
1539
  */
1501
- const Select = forwardRef(({ options, children, className, containerClassName, variant = 'themed', ...rest }, ref) => {
1502
- const isThemed = variant === 'themed' || variant === 'dashboard';
1503
- const selectEl = (jsxRuntimeExports.jsx("select", { ref: ref, className: cn(isThemed
1504
- ? 'mds-select-themed'
1505
- : 'mds-input mds-select', className), ...rest, children: options
1506
- ? options.map(opt => (jsxRuntimeExports.jsx("option", { value: opt.value, children: opt.label }, opt.value)))
1507
- : children }));
1508
- if (isThemed) {
1509
- return (jsxRuntimeExports.jsx("div", { className: cn('mds-dropdown', containerClassName), children: selectEl }));
1510
- }
1511
- return selectEl;
1540
+ const Select = forwardRef(({ options, value, onChange, placeholder = 'Select...', variant = 'themed', size = 'md', disabled = false, loading = false, error = false, className, dropdownClassName, id, name, 'aria-label': ariaLabel, fullWidth = false, searchable = false, searchPlaceholder = 'Search...', maxHeight = 300, zIndex = 1050, }, ref) => {
1541
+ const [isOpen, setIsOpen] = useState(false);
1542
+ const [searchTerm, setSearchTerm] = useState('');
1543
+ const [highlightedIndex, setHighlightedIndex] = useState(-1);
1544
+ const [mounted, setMounted] = useState(false);
1545
+ const triggerRef = useRef(null);
1546
+ const dropdownRef = useRef(null);
1547
+ const searchInputRef = useRef(null);
1548
+ const listRef = useRef(null);
1549
+ // Combine refs
1550
+ const combinedRef = (el) => {
1551
+ triggerRef.current = el;
1552
+ if (typeof ref === 'function') {
1553
+ ref(el);
1554
+ }
1555
+ else if (ref) {
1556
+ ref.current = el;
1557
+ }
1558
+ };
1559
+ // Client-side only
1560
+ useEffect(() => {
1561
+ setMounted(true);
1562
+ }, []);
1563
+ // Filter options based on search
1564
+ const filteredOptions = useMemo(() => {
1565
+ if (!searchTerm)
1566
+ return options;
1567
+ const term = searchTerm.toLowerCase();
1568
+ return options.filter(opt => {
1569
+ const label = typeof opt.label === 'string' ? opt.label : String(opt.value);
1570
+ return label.toLowerCase().includes(term);
1571
+ });
1572
+ }, [options, searchTerm]);
1573
+ // Get selected option label
1574
+ const selectedOption = useMemo(() => {
1575
+ return options.find(opt => opt.value === value);
1576
+ }, [options, value]);
1577
+ // Handle dropdown positioning
1578
+ const [dropdownPosition, setDropdownPosition] = useState({ top: 0, left: 0, width: 0 });
1579
+ const updateDropdownPosition = useCallback(() => {
1580
+ if (!triggerRef.current)
1581
+ return;
1582
+ const rect = triggerRef.current.getBoundingClientRect();
1583
+ const viewportHeight = window.innerHeight;
1584
+ const spaceBelow = viewportHeight - rect.bottom;
1585
+ const spaceAbove = rect.top;
1586
+ // Determine if dropdown should open above or below
1587
+ const dropdownHeight = Math.min(maxHeight, filteredOptions.length * 40 + (searchable ? 48 : 0));
1588
+ const openAbove = spaceBelow < dropdownHeight && spaceAbove > spaceBelow;
1589
+ setDropdownPosition({
1590
+ top: openAbove ? rect.top - dropdownHeight : rect.bottom + 4,
1591
+ left: rect.left,
1592
+ width: rect.width,
1593
+ });
1594
+ }, [maxHeight, filteredOptions.length, searchable]);
1595
+ // Open dropdown
1596
+ const openDropdown = useCallback(() => {
1597
+ if (disabled || loading)
1598
+ return;
1599
+ updateDropdownPosition();
1600
+ setIsOpen(true);
1601
+ setSearchTerm('');
1602
+ setHighlightedIndex(value ? filteredOptions.findIndex(opt => opt.value === value) : 0);
1603
+ }, [disabled, loading, updateDropdownPosition, value, filteredOptions]);
1604
+ // Close dropdown
1605
+ const closeDropdown = useCallback(() => {
1606
+ setIsOpen(false);
1607
+ setSearchTerm('');
1608
+ setHighlightedIndex(-1);
1609
+ triggerRef.current?.focus();
1610
+ }, []);
1611
+ // Handle option select
1612
+ const handleSelect = useCallback((optionValue) => {
1613
+ // Safety check: ensure we're passing a string, not an object
1614
+ const safeValue = typeof optionValue === 'string' ? optionValue : String(optionValue);
1615
+ onChange?.(safeValue);
1616
+ closeDropdown();
1617
+ }, [onChange, closeDropdown]);
1618
+ // Keyboard navigation
1619
+ const handleKeyDown = useCallback((e) => {
1620
+ if (disabled || loading)
1621
+ return;
1622
+ switch (e.key) {
1623
+ case 'Enter':
1624
+ case ' ':
1625
+ e.preventDefault();
1626
+ if (isOpen && highlightedIndex >= 0 && filteredOptions[highlightedIndex]) {
1627
+ const opt = filteredOptions[highlightedIndex];
1628
+ if (!opt.disabled) {
1629
+ handleSelect(opt.value);
1630
+ }
1631
+ }
1632
+ else if (!isOpen) {
1633
+ openDropdown();
1634
+ }
1635
+ break;
1636
+ case 'ArrowDown':
1637
+ e.preventDefault();
1638
+ if (!isOpen) {
1639
+ openDropdown();
1640
+ }
1641
+ else {
1642
+ setHighlightedIndex(prev => {
1643
+ const next = prev + 1;
1644
+ return next >= filteredOptions.length ? 0 : next;
1645
+ });
1646
+ }
1647
+ break;
1648
+ case 'ArrowUp':
1649
+ e.preventDefault();
1650
+ if (isOpen) {
1651
+ setHighlightedIndex(prev => {
1652
+ const next = prev - 1;
1653
+ return next < 0 ? filteredOptions.length - 1 : next;
1654
+ });
1655
+ }
1656
+ break;
1657
+ case 'Escape':
1658
+ e.preventDefault();
1659
+ closeDropdown();
1660
+ break;
1661
+ case 'Tab':
1662
+ if (isOpen) {
1663
+ closeDropdown();
1664
+ }
1665
+ break;
1666
+ case 'Home':
1667
+ if (isOpen) {
1668
+ e.preventDefault();
1669
+ setHighlightedIndex(0);
1670
+ }
1671
+ break;
1672
+ case 'End':
1673
+ if (isOpen) {
1674
+ e.preventDefault();
1675
+ setHighlightedIndex(filteredOptions.length - 1);
1676
+ }
1677
+ break;
1678
+ }
1679
+ }, [disabled, loading, isOpen, highlightedIndex, filteredOptions, handleSelect, openDropdown, closeDropdown]);
1680
+ // Click outside to close
1681
+ useEffect(() => {
1682
+ if (!isOpen)
1683
+ return;
1684
+ const handleClickOutside = (e) => {
1685
+ if (triggerRef.current?.contains(e.target) ||
1686
+ dropdownRef.current?.contains(e.target)) {
1687
+ return;
1688
+ }
1689
+ closeDropdown();
1690
+ };
1691
+ document.addEventListener('mousedown', handleClickOutside);
1692
+ return () => document.removeEventListener('mousedown', handleClickOutside);
1693
+ }, [isOpen, closeDropdown]);
1694
+ // Update position on scroll/resize
1695
+ useEffect(() => {
1696
+ if (!isOpen)
1697
+ return;
1698
+ const handleUpdate = () => updateDropdownPosition();
1699
+ window.addEventListener('scroll', handleUpdate, true);
1700
+ window.addEventListener('resize', handleUpdate);
1701
+ return () => {
1702
+ window.removeEventListener('scroll', handleUpdate, true);
1703
+ window.removeEventListener('resize', handleUpdate);
1704
+ };
1705
+ }, [isOpen, updateDropdownPosition]);
1706
+ // Focus search input when dropdown opens
1707
+ useEffect(() => {
1708
+ if (isOpen && searchable && searchInputRef.current) {
1709
+ searchInputRef.current.focus();
1710
+ }
1711
+ }, [isOpen, searchable]);
1712
+ // Scroll highlighted option into view
1713
+ useEffect(() => {
1714
+ if (!isOpen || highlightedIndex < 0 || !listRef.current)
1715
+ return;
1716
+ const highlighted = listRef.current.children[highlightedIndex];
1717
+ if (highlighted) {
1718
+ highlighted.scrollIntoView({ block: 'nearest' });
1719
+ }
1720
+ }, [isOpen, highlightedIndex]);
1721
+ // Size classes
1722
+ const sizeClasses = {
1723
+ sm: 'mds-select--sm',
1724
+ md: 'mds-select--md',
1725
+ lg: 'mds-select--lg',
1726
+ };
1727
+ // Variant classes
1728
+ const variantClasses = {
1729
+ base: 'mds-select--base',
1730
+ themed: 'mds-select--themed',
1731
+ dashboard: 'mds-select--themed',
1732
+ };
1733
+ const triggerClasses = cn('mds-select-trigger', sizeClasses[size], variantClasses[variant], isOpen && 'mds-select-trigger--open', disabled && 'mds-select-trigger--disabled', loading && 'mds-select-trigger--loading', error && 'mds-select-trigger--error', fullWidth && 'mds-select-trigger--full-width', className);
1734
+ const dropdownClasses = cn('mds-select-dropdown', variantClasses[variant], dropdownClassName);
1735
+ // Hidden input for form submission
1736
+ const hiddenInput = name ? (jsxRuntimeExports.jsx("input", { type: "hidden", name: name, value: value || '' })) : null;
1737
+ // Dropdown portal content
1738
+ const dropdownContent = isOpen && mounted ? createPortal(jsxRuntimeExports.jsxs("div", { ref: dropdownRef, className: dropdownClasses, style: {
1739
+ position: 'fixed',
1740
+ top: dropdownPosition.top,
1741
+ left: dropdownPosition.left,
1742
+ width: dropdownPosition.width,
1743
+ maxHeight,
1744
+ zIndex,
1745
+ }, role: "listbox", "aria-label": ariaLabel || placeholder, children: [searchable && (jsxRuntimeExports.jsxs("div", { className: "mds-select-search", children: [jsxRuntimeExports.jsx("input", { ref: searchInputRef, type: "text", className: "mds-select-search__input", placeholder: searchPlaceholder, value: searchTerm, onChange: (e) => {
1746
+ setSearchTerm(e.target.value);
1747
+ setHighlightedIndex(0);
1748
+ }, onKeyDown: handleKeyDown, "aria-label": "Search options" }), jsxRuntimeExports.jsx("svg", { className: "mds-select-search__icon", viewBox: "0 0 20 20", fill: "currentColor", children: jsxRuntimeExports.jsx("path", { fillRule: "evenodd", d: "M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z", clipRule: "evenodd" }) })] })), jsxRuntimeExports.jsx("ul", { ref: listRef, className: "mds-select-options", children: filteredOptions.length === 0 ? (jsxRuntimeExports.jsx("li", { className: "mds-select-option mds-select-option--empty", children: "No options found" })) : (filteredOptions.map((option, index) => (jsxRuntimeExports.jsxs("li", { className: cn('mds-select-option', option.value === value && 'mds-select-option--selected', index === highlightedIndex && 'mds-select-option--highlighted', option.disabled && 'mds-select-option--disabled'), role: "option", "aria-selected": option.value === value, "aria-disabled": option.disabled, onClick: () => {
1749
+ if (!option.disabled) {
1750
+ handleSelect(option.value);
1751
+ }
1752
+ }, onMouseEnter: () => {
1753
+ if (!option.disabled) {
1754
+ setHighlightedIndex(index);
1755
+ }
1756
+ }, children: [jsxRuntimeExports.jsx("span", { className: "mds-select-option__label", children: option.label }), option.value === value && (jsxRuntimeExports.jsx("svg", { className: "mds-select-option__check", viewBox: "0 0 20 20", fill: "currentColor", children: jsxRuntimeExports.jsx("path", { fillRule: "evenodd", d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z", clipRule: "evenodd" }) }))] }, option.value)))) })] }), document.body) : null;
1757
+ return (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [hiddenInput, jsxRuntimeExports.jsxs("button", { ref: combinedRef, type: "button", id: id, className: triggerClasses, onClick: () => isOpen ? closeDropdown() : openDropdown(), onKeyDown: handleKeyDown, disabled: disabled || loading, "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-label": ariaLabel, "aria-invalid": error, children: [jsxRuntimeExports.jsx("span", { className: cn('mds-select-trigger__value', !selectedOption && 'mds-select-trigger__placeholder'), children: loading ? 'Loading...' : (selectedOption?.label || placeholder) }), jsxRuntimeExports.jsx("span", { className: "mds-select-trigger__icon", children: loading ? (jsxRuntimeExports.jsx("svg", { className: "mds-select-spinner", viewBox: "0 0 24 24", fill: "none", children: jsxRuntimeExports.jsx("circle", { cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeDasharray: "32", strokeDashoffset: "32", children: jsxRuntimeExports.jsx("animate", { attributeName: "stroke-dashoffset", values: "32;0", dur: "1s", repeatCount: "indefinite" }) }) })) : (jsxRuntimeExports.jsx("svg", { className: "mds-select-chevron", viewBox: "0 0 20 20", fill: "currentColor", children: jsxRuntimeExports.jsx("path", { fillRule: "evenodd", d: "M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z", clipRule: "evenodd" }) })) })] }), dropdownContent] }));
1512
1758
  });
1513
1759
  Select.displayName = 'Select';
1514
1760
 
@@ -2184,12 +2430,82 @@ const getTableConfig = (type) => {
2184
2430
  }
2185
2431
  };
2186
2432
 
2433
+ /**
2434
+ * Theme-aware styles generator - Liquid Glass pattern
2435
+ *
2436
+ * Light theme: White semi-transparent overlay to keep background bright
2437
+ * Dark theme: Dark overlay for contrast
2438
+ */
2439
+ function getStyles(isDark) {
2440
+ return {
2441
+ // Overlay behind the modal
2442
+ overlay: {
2443
+ position: 'fixed',
2444
+ inset: 0,
2445
+ zIndex: 9999,
2446
+ display: 'flex',
2447
+ alignItems: 'center',
2448
+ justifyContent: 'center',
2449
+ padding: 20,
2450
+ // Light theme: white overlay to keep brightness
2451
+ // Dark theme: dark overlay for contrast
2452
+ backgroundColor: isDark ? 'rgba(0, 0, 0, 0.6)' : 'rgba(255, 255, 255, 0.4)',
2453
+ backdropFilter: 'blur(8px)',
2454
+ WebkitBackdropFilter: 'blur(8px)',
2455
+ },
2456
+ // Modal card - Liquid Glass
2457
+ card: {
2458
+ position: 'relative',
2459
+ maxWidth: 640,
2460
+ width: '100%',
2461
+ borderRadius: 24,
2462
+ overflow: 'hidden',
2463
+ // Light theme: Liquid Glass transparency
2464
+ // Dark theme: subtle glass effect
2465
+ background: isDark
2466
+ ? 'rgba(255, 255, 255, 0.03)'
2467
+ : 'rgba(255, 255, 255, 0.45)',
2468
+ // Liquid Glass: Strong blur + saturation
2469
+ backdropFilter: 'blur(var(--mds-liquid-blur-xl, 24px)) saturate(var(--mds-liquid-saturate-vibrant, 140%))',
2470
+ WebkitBackdropFilter: 'blur(var(--mds-liquid-blur-xl, 24px)) saturate(var(--mds-liquid-saturate-vibrant, 140%))',
2471
+ // Liquid Glass: Subtle border for edge definition
2472
+ border: isDark
2473
+ ? '1px solid rgba(255, 255, 255, 0.08)'
2474
+ : '1px solid rgba(255, 255, 255, 0.9)',
2475
+ // Liquid Glass: Layered shadows + inner glow
2476
+ boxShadow: isDark
2477
+ ? 'var(--mds-liquid-shadow-elevated, 0 12px 40px rgba(0, 0, 0, 0.3)), var(--mds-liquid-glow-edge, inset 0 1px 0 rgba(255, 255, 255, 0.1))'
2478
+ : '0 8px 32px rgba(0, 0, 0, 0.08), inset 0 1px 0 rgba(255, 255, 255, 1)',
2479
+ // Liquid Glass: Smooth transition
2480
+ transition: 'var(--mds-liquid-transition, all 0.25s cubic-bezier(0.25, 0.1, 0.25, 1))',
2481
+ },
2482
+ };
2483
+ }
2187
2484
  function Modal({ open, onClose, closeOnOverlay = true, children, className, style }) {
2188
2485
  const [mounted, setMounted] = useState(false);
2189
2486
  const [visible, setVisible] = useState(false);
2190
2487
  const [renderPortal, setRenderPortal] = useState(false);
2488
+ const [isDarkTheme, setIsDarkTheme] = useState(true);
2191
2489
  const containerRef = useRef(null);
2192
2490
  useEffect(() => setMounted(true), []);
2491
+ // Detect theme - EXACT same pattern as ProfileCard.tsx
2492
+ useEffect(() => {
2493
+ const checkTheme = () => {
2494
+ const theme = document.documentElement.getAttribute('data-theme');
2495
+ setIsDarkTheme(theme !== 'light');
2496
+ };
2497
+ checkTheme();
2498
+ // Watch for theme changes
2499
+ const observer = new MutationObserver((mutations) => {
2500
+ mutations.forEach((mutation) => {
2501
+ if (mutation.attributeName === 'data-theme') {
2502
+ checkTheme();
2503
+ }
2504
+ });
2505
+ });
2506
+ observer.observe(document.documentElement, { attributes: true });
2507
+ return () => observer.disconnect();
2508
+ }, []);
2193
2509
  useEffect(() => {
2194
2510
  if (!mounted)
2195
2511
  return;
@@ -2215,27 +2531,21 @@ function Modal({ open, onClose, closeOnOverlay = true, children, className, styl
2215
2531
  }, [open, onClose]);
2216
2532
  if (!mounted || (!renderPortal && !open))
2217
2533
  return null;
2534
+ // Get theme-aware styles (same pattern as ProfileCard)
2535
+ const styles = getStyles(isDarkTheme);
2218
2536
  const overlayStyle = {
2219
- position: 'fixed',
2220
- inset: 0,
2221
- backgroundColor: 'rgba(0, 0, 0, 0.7)',
2222
- display: 'flex',
2223
- alignItems: 'center',
2224
- justifyContent: 'center',
2225
- zIndex: 1000,
2226
- padding: '16px',
2537
+ ...styles.overlay,
2227
2538
  opacity: visible ? 1 : 0,
2228
- transition: 'opacity 200ms ease'
2539
+ transition: 'opacity 200ms ease',
2229
2540
  };
2230
- const innerStyle = {
2231
- maxWidth: '640px',
2232
- width: '100%',
2233
- transform: visible ? 'scale(1)' : 'scale(0.98)',
2541
+ const cardStyle = {
2542
+ ...styles.card,
2543
+ transform: visible ? 'scale(1)' : 'scale(0.96)',
2234
2544
  opacity: visible ? 1 : 0,
2235
- transition: 'opacity 200ms ease, transform 200ms ease',
2236
- ...style
2545
+ transition: 'opacity 200ms ease, transform 200ms cubic-bezier(0.4, 0, 0.2, 1)',
2546
+ ...style,
2237
2547
  };
2238
- const content = (jsxRuntimeExports.jsx("div", { className: "modal-overlay mds-modal-overlay", style: overlayStyle, onClick: closeOnOverlay ? onClose : undefined, "aria-modal": "true", role: "dialog", children: jsxRuntimeExports.jsx("div", { className: className, style: innerStyle, onClick: (e) => e.stopPropagation(), ref: (el) => (containerRef.current = el), children: children }) }));
2548
+ const content = (jsxRuntimeExports.jsx("div", { className: "modal-overlay mds-modal-overlay", style: overlayStyle, onClick: closeOnOverlay ? onClose : undefined, "aria-modal": "true", role: "dialog", children: jsxRuntimeExports.jsx("div", { className: className, style: cardStyle, onClick: (e) => e.stopPropagation(), ref: (el) => (containerRef.current = el), children: children }) }));
2239
2549
  return createPortal(content, document.body);
2240
2550
  }
2241
2551