@pk-design/react-tailwind 1.10.2 → 1.11.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/cjs/components/MobileHeader/MobileHeader.js +5 -4
- package/dist/cjs/components/MobileHeader/MobileHeader.js.map +1 -1
- package/dist/cjs/components/Toast/Toast.js +3 -1
- package/dist/cjs/components/Toast/Toast.js.map +1 -1
- package/dist/cjs/components/Toast/ToastProvider.js +10 -5
- package/dist/cjs/components/Toast/ToastProvider.js.map +1 -1
- package/dist/es/components/MobileHeader/MobileHeader.js +5 -4
- package/dist/es/components/MobileHeader/MobileHeader.js.map +1 -1
- package/dist/es/components/Toast/Toast.js +3 -1
- package/dist/es/components/Toast/Toast.js.map +1 -1
- package/dist/es/components/Toast/ToastProvider.js +10 -5
- package/dist/es/components/Toast/ToastProvider.js.map +1 -1
- package/dist/react-tailwind.cjs.js +18 -11
- package/dist/react-tailwind.cjs.js.map +1 -1
- package/dist/react-tailwind.es.js +18 -11
- package/dist/react-tailwind.es.js.map +1 -1
- package/dist/types/components/Toast/Toast.types.d.ts +18 -2
- package/package.json +1 -1
|
@@ -41,12 +41,13 @@ function MobileHeader({ title, subtitle, titleAlign = 'center', onBack, backLabe
|
|
|
41
41
|
};
|
|
42
42
|
return (jsxRuntime.jsxs("header", { className: buildClassname.buildClassName('w-full transition-colors select-none z-30', sticky && 'sticky top-0', transparent
|
|
43
43
|
? 'bg-white/80 dark:bg-gray-900/80 backdrop-blur-md border-b border-[var(--ui-border-muted)]'
|
|
44
|
-
: 'bg-white dark:bg-gray-900 border-b border-[var(--ui-border)] shadow-xs', 'pt-[env(safe-area-inset-top,0px)]', className), "data-testid": "mobile-header", children: [jsxRuntime.jsx("div", { className: "h-14 px-4 flex items-center justify-between gap-
|
|
44
|
+
: 'bg-white dark:bg-gray-900 border-b border-[var(--ui-border)] shadow-xs', 'pt-[env(safe-area-inset-top,0px)]', className), "data-testid": "mobile-header", children: [jsxRuntime.jsx("div", { className: "h-14 px-4 flex items-center justify-between gap-2 relative", children: searchable && isSearchActive ? (jsxRuntime.jsxs("div", { className: "flex-1 flex items-center gap-2", "data-testid": "mobile-header-search-container", children: [jsxRuntime.jsxs("div", { className: "relative flex-1 flex items-center", children: [jsxRuntime.jsx("span", { className: "absolute left-3 pointer-events-none", children: jsxRuntime.jsx(SearchIcon, {}) }), jsxRuntime.jsx("input", { type: "text", value: searchValue, onChange: handleSearchInput, placeholder: searchPlaceholder, autoFocus: true, className: "w-full pl-9 pr-8 py-1.5 text-xs bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-white rounded-full border border-transparent focus:border-blue-500 focus:outline-hidden transition-all", "data-testid": "mobile-header-search-input" }), searchValue && (jsxRuntime.jsx("button", { type: "button", onClick: handleClearSearch, "aria-label": "Clear search", className: "absolute right-2.5 p-0.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 cursor-pointer", children: jsxRuntime.jsx(ClearIcon, {}) }))] }), jsxRuntime.jsx("button", { type: "button", onClick: () => setIsSearchActive(false), className: "text-xs font-semibold text-blue-600 dark:text-blue-400 px-2 py-1.5 rounded-lg hover:bg-blue-50 dark:hover:bg-blue-950/40 cursor-pointer", children: "Cancel" })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [hasLeadingSlot && (jsxRuntime.jsx("div", { className: buildClassname.buildClassName('flex items-center gap-1 shrink-0 z-10',
|
|
45
45
|
// A Back button is its own 40px target; reserve the width only for other content.
|
|
46
46
|
!onBack && 'min-w-[40px]'), "data-testid": "mobile-header-leading", children: onBack ? (jsxRuntime.jsxs("button", { type: "button", onClick: onBack, "aria-label": backLabel,
|
|
47
|
-
// 40px touch target hanging into the row padding
|
|
48
|
-
//
|
|
49
|
-
|
|
47
|
+
// 40px-tall touch target hanging into the row padding: the chevron
|
|
48
|
+
// sits on the content edge, with no right padding, so the title
|
|
49
|
+
// follows ~10px after it.
|
|
50
|
+
className: "flex min-h-10 min-w-8 items-center justify-start gap-0.5 pl-2.5 pr-0 sm:pr-2 -ml-2.5 text-blue-600 dark:text-blue-400 hover:text-blue-700 rounded-xl transition-colors cursor-pointer", "data-testid": "mobile-header-back-btn", children: [jsxRuntime.jsx(ChevronLeftIcon, {}), jsxRuntime.jsx("span", { className: "text-xs font-semibold hidden sm:inline", children: backLabel })] })) : (leading) })), jsxRuntime.jsxs("div", { className: buildClassname.buildClassName('flex-1 min-w-0',
|
|
50
51
|
// The row's gap already separates a left title from a leading element;
|
|
51
52
|
// a centred title keeps even padding.
|
|
52
53
|
titleAlign === 'center' ? 'px-2' : 'pr-2', titleAlign === 'center'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MobileHeader.js","sources":["../../../../../src/components/MobileHeader/MobileHeader.tsx"],"sourcesContent":["import React, { useState } from 'react'\nimport type { MobileHeaderProps, MobileHeaderAction } from './MobileHeader.types'\nimport { buildClassName } from '../../utils/build-classname'\nimport { ActionSheet } from '../ActionSheet'\n\nfunction ChevronLeftIcon() {\n return (\n <svg className=\"size-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2.5} d=\"M15 19l-7-7 7-7\" />\n </svg>\n )\n}\n\nfunction SearchIcon() {\n return (\n <svg className=\"size-4 text-gray-400\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"\n />\n </svg>\n )\n}\n\nfunction MoreIcon() {\n return (\n <svg className=\"size-5\" fill=\"currentColor\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"5\" r=\"1.75\" />\n <circle cx=\"12\" cy=\"12\" r=\"1.75\" />\n <circle cx=\"12\" cy=\"19\" r=\"1.75\" />\n </svg>\n )\n}\n\nfunction ClearIcon() {\n return (\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n )\n}\n\nexport function MobileHeader({\n title,\n subtitle,\n titleAlign = 'center',\n onBack,\n backLabel = 'Back',\n leading,\n actions,\n maxVisibleActions,\n overflowLabel = 'More actions',\n trailing,\n searchable = false,\n searchValue: externalSearchValue,\n onSearchChange,\n searchPlaceholder = 'Search...',\n sticky = true,\n transparent = false,\n bottomSlot,\n className = '',\n}: MobileHeaderProps) {\n const [isSearchActive, setIsSearchActive] = useState(false)\n const [internalSearchValue, setInternalSearchValue] = useState('')\n const [isOverflowOpen, setIsOverflowOpen] = useState(false)\n const hasLeadingSlot = Boolean(onBack || leading) || titleAlign === 'center'\n\n // The overflow button takes one of the visible slots.\n const allActions = actions ?? []\n const shouldOverflow = maxVisibleActions !== undefined && allActions.length > maxVisibleActions\n const visibleActions = shouldOverflow\n ? allActions.slice(0, Math.max(maxVisibleActions - 1, 0))\n : allActions\n const overflowActions = shouldOverflow ? allActions.slice(visibleActions.length) : []\n\n const searchValue = externalSearchValue !== undefined ? externalSearchValue : internalSearchValue\n\n const handleSearchInput = (e: React.ChangeEvent<HTMLInputElement>) => {\n const val = e.target.value\n setInternalSearchValue(val)\n onSearchChange?.(val)\n }\n\n const handleClearSearch = () => {\n setInternalSearchValue('')\n onSearchChange?.('')\n }\n\n return (\n <header\n className={buildClassName(\n 'w-full transition-colors select-none z-30',\n sticky && 'sticky top-0',\n transparent\n ? 'bg-white/80 dark:bg-gray-900/80 backdrop-blur-md border-b border-[var(--ui-border-muted)]'\n : 'bg-white dark:bg-gray-900 border-b border-[var(--ui-border)] shadow-xs',\n 'pt-[env(safe-area-inset-top,0px)]',\n className,\n )}\n data-testid=\"mobile-header\"\n >\n {/* Main Header Row */}\n <div className=\"h-14 px-4 flex items-center justify-between gap-3 relative\">\n {/* Search Mode Header View */}\n {searchable && isSearchActive ? (\n <div\n className=\"flex-1 flex items-center gap-2\"\n data-testid=\"mobile-header-search-container\"\n >\n <div className=\"relative flex-1 flex items-center\">\n <span className=\"absolute left-3 pointer-events-none\">\n <SearchIcon />\n </span>\n <input\n type=\"text\"\n value={searchValue}\n onChange={handleSearchInput}\n placeholder={searchPlaceholder}\n autoFocus\n className=\"w-full pl-9 pr-8 py-1.5 text-xs bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-white rounded-full border border-transparent focus:border-blue-500 focus:outline-hidden transition-all\"\n data-testid=\"mobile-header-search-input\"\n />\n {searchValue && (\n <button\n type=\"button\"\n onClick={handleClearSearch}\n aria-label=\"Clear search\"\n className=\"absolute right-2.5 p-0.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 cursor-pointer\"\n >\n <ClearIcon />\n </button>\n )}\n </div>\n <button\n type=\"button\"\n onClick={() => setIsSearchActive(false)}\n className=\"text-xs font-semibold text-blue-600 dark:text-blue-400 px-2 py-1.5 rounded-lg hover:bg-blue-50 dark:hover:bg-blue-950/40 cursor-pointer\"\n >\n Cancel\n </button>\n </div>\n ) : (\n <>\n {/* Left Leading / Back Section. Omitted for a left-aligned title with nothing\n before it, so the title lines up with the page content; kept for a centred\n title, where the empty slot balances the actions. */}\n {hasLeadingSlot && (\n <div\n className={buildClassName(\n 'flex items-center gap-1 shrink-0 z-10',\n // A Back button is its own 40px target; reserve the width only for other content.\n !onBack && 'min-w-[40px]',\n )}\n data-testid=\"mobile-header-leading\"\n >\n {onBack ? (\n <button\n type=\"button\"\n onClick={onBack}\n aria-label={backLabel}\n // 40px touch target hanging into the row padding, so the chevron\n // itself sits on the content edge and the title follows closely.\n className=\"flex min-h-10 min-w-10 items-center justify-center gap-0.5 px-2.5 -ml-2.5 text-blue-600 dark:text-blue-400 hover:text-blue-700 rounded-xl transition-colors cursor-pointer\"\n data-testid=\"mobile-header-back-btn\"\n >\n <ChevronLeftIcon />\n <span className=\"text-xs font-semibold hidden sm:inline\">{backLabel}</span>\n </button>\n ) : (\n leading\n )}\n </div>\n )}\n\n {/* Title & Subtitle Section */}\n <div\n className={buildClassName(\n 'flex-1 min-w-0',\n // The row's gap already separates a left title from a leading element;\n // a centred title keeps even padding.\n titleAlign === 'center' ? 'px-2' : 'pr-2',\n titleAlign === 'center'\n ? 'absolute inset-x-12 text-center pointer-events-none flex flex-col items-center justify-center'\n : 'text-left',\n )}\n >\n {title && (\n <div\n className=\"text-sm font-bold text-gray-900 dark:text-white truncate pointer-events-auto\"\n data-testid=\"mobile-header-title\"\n >\n {title}\n </div>\n )}\n {subtitle && (\n <div\n className=\"text-[11px] text-gray-500 dark:text-gray-400 truncate pointer-events-auto\"\n data-testid=\"mobile-header-subtitle\"\n >\n {subtitle}\n </div>\n )}\n </div>\n\n {/* Right Actions Section */}\n <div className=\"flex items-center gap-1 min-w-[40px] shrink-0 justify-end z-10\">\n {searchable && (\n <button\n type=\"button\"\n onClick={() => setIsSearchActive(true)}\n aria-label=\"Search\"\n className=\"p-2 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-xl transition-colors cursor-pointer\"\n data-testid=\"mobile-header-search-toggle\"\n >\n <SearchIcon />\n </button>\n )}\n\n {visibleActions.map((action: MobileHeaderAction) => {\n const badgeColor = {\n danger: 'bg-red-500 text-white',\n info: 'bg-blue-500 text-white',\n success: 'bg-emerald-500 text-white',\n }[action.badgeVariant || 'danger']\n\n return (\n <button\n key={action.id}\n type=\"button\"\n disabled={action.disabled}\n onClick={action.onClick}\n aria-label={action.label}\n className={buildClassName(\n 'p-2 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-xl transition-colors relative cursor-pointer',\n action.disabled && 'opacity-40 cursor-not-allowed pointer-events-none',\n )}\n data-testid={`mobile-header-action-${action.id}`}\n >\n <span className=\"text-base\">{action.icon}</span>\n {action.badge !== undefined && action.badge !== null && (\n <span\n className={buildClassName(\n 'absolute top-1 right-1 min-w-[14px] h-3.5 px-0.5 rounded-full text-[9px] font-bold flex items-center justify-center shadow-xs',\n badgeColor,\n )}\n data-testid={`mobile-header-badge-${action.id}`}\n >\n {action.badge}\n </span>\n )}\n </button>\n )\n })}\n\n {overflowActions.length > 0 && (\n <button\n type=\"button\"\n onClick={() => setIsOverflowOpen(true)}\n aria-label={overflowLabel}\n aria-haspopup=\"dialog\"\n aria-expanded={isOverflowOpen}\n className=\"p-2 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-xl transition-colors cursor-pointer\"\n data-testid=\"mobile-header-overflow\"\n >\n <MoreIcon />\n </button>\n )}\n\n {trailing}\n </div>\n </>\n )}\n </div>\n\n {overflowActions.length > 0 && (\n <ActionSheet\n isOpen={isOverflowOpen}\n onClose={() => setIsOverflowOpen(false)}\n actions={overflowActions.map((action) => ({\n id: action.id,\n label: action.label,\n icon: action.icon,\n disabled: action.disabled,\n onClick: action.onClick,\n }))}\n />\n )}\n\n {/* Optional Bottom Sub-Header Slot (e.g. Category tabs or filter bar) */}\n {bottomSlot && (\n <div\n className=\"px-3 py-2 border-t border-[var(--ui-border-muted)]\"\n data-testid=\"mobile-header-bottom-slot\"\n >\n {bottomSlot}\n </div>\n )}\n </header>\n )\n}\n\nMobileHeader.displayName = 'MobileHeader'\n"],"names":["_jsx","_jsxs","useState","buildClassName","_Fragment","ActionSheet"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MobileHeader.js","sources":["../../../../../src/components/MobileHeader/MobileHeader.tsx"],"sourcesContent":["import React, { useState } from 'react'\nimport type { MobileHeaderProps, MobileHeaderAction } from './MobileHeader.types'\nimport { buildClassName } from '../../utils/build-classname'\nimport { ActionSheet } from '../ActionSheet'\n\nfunction ChevronLeftIcon() {\n return (\n <svg className=\"size-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2.5} d=\"M15 19l-7-7 7-7\" />\n </svg>\n )\n}\n\nfunction SearchIcon() {\n return (\n <svg className=\"size-4 text-gray-400\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"\n />\n </svg>\n )\n}\n\nfunction MoreIcon() {\n return (\n <svg className=\"size-5\" fill=\"currentColor\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"5\" r=\"1.75\" />\n <circle cx=\"12\" cy=\"12\" r=\"1.75\" />\n <circle cx=\"12\" cy=\"19\" r=\"1.75\" />\n </svg>\n )\n}\n\nfunction ClearIcon() {\n return (\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n )\n}\n\nexport function MobileHeader({\n title,\n subtitle,\n titleAlign = 'center',\n onBack,\n backLabel = 'Back',\n leading,\n actions,\n maxVisibleActions,\n overflowLabel = 'More actions',\n trailing,\n searchable = false,\n searchValue: externalSearchValue,\n onSearchChange,\n searchPlaceholder = 'Search...',\n sticky = true,\n transparent = false,\n bottomSlot,\n className = '',\n}: MobileHeaderProps) {\n const [isSearchActive, setIsSearchActive] = useState(false)\n const [internalSearchValue, setInternalSearchValue] = useState('')\n const [isOverflowOpen, setIsOverflowOpen] = useState(false)\n const hasLeadingSlot = Boolean(onBack || leading) || titleAlign === 'center'\n\n // The overflow button takes one of the visible slots.\n const allActions = actions ?? []\n const shouldOverflow = maxVisibleActions !== undefined && allActions.length > maxVisibleActions\n const visibleActions = shouldOverflow\n ? allActions.slice(0, Math.max(maxVisibleActions - 1, 0))\n : allActions\n const overflowActions = shouldOverflow ? allActions.slice(visibleActions.length) : []\n\n const searchValue = externalSearchValue !== undefined ? externalSearchValue : internalSearchValue\n\n const handleSearchInput = (e: React.ChangeEvent<HTMLInputElement>) => {\n const val = e.target.value\n setInternalSearchValue(val)\n onSearchChange?.(val)\n }\n\n const handleClearSearch = () => {\n setInternalSearchValue('')\n onSearchChange?.('')\n }\n\n return (\n <header\n className={buildClassName(\n 'w-full transition-colors select-none z-30',\n sticky && 'sticky top-0',\n transparent\n ? 'bg-white/80 dark:bg-gray-900/80 backdrop-blur-md border-b border-[var(--ui-border-muted)]'\n : 'bg-white dark:bg-gray-900 border-b border-[var(--ui-border)] shadow-xs',\n 'pt-[env(safe-area-inset-top,0px)]',\n className,\n )}\n data-testid=\"mobile-header\"\n >\n {/* Main Header Row */}\n <div className=\"h-14 px-4 flex items-center justify-between gap-2 relative\">\n {/* Search Mode Header View */}\n {searchable && isSearchActive ? (\n <div\n className=\"flex-1 flex items-center gap-2\"\n data-testid=\"mobile-header-search-container\"\n >\n <div className=\"relative flex-1 flex items-center\">\n <span className=\"absolute left-3 pointer-events-none\">\n <SearchIcon />\n </span>\n <input\n type=\"text\"\n value={searchValue}\n onChange={handleSearchInput}\n placeholder={searchPlaceholder}\n autoFocus\n className=\"w-full pl-9 pr-8 py-1.5 text-xs bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-white rounded-full border border-transparent focus:border-blue-500 focus:outline-hidden transition-all\"\n data-testid=\"mobile-header-search-input\"\n />\n {searchValue && (\n <button\n type=\"button\"\n onClick={handleClearSearch}\n aria-label=\"Clear search\"\n className=\"absolute right-2.5 p-0.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 cursor-pointer\"\n >\n <ClearIcon />\n </button>\n )}\n </div>\n <button\n type=\"button\"\n onClick={() => setIsSearchActive(false)}\n className=\"text-xs font-semibold text-blue-600 dark:text-blue-400 px-2 py-1.5 rounded-lg hover:bg-blue-50 dark:hover:bg-blue-950/40 cursor-pointer\"\n >\n Cancel\n </button>\n </div>\n ) : (\n <>\n {/* Left Leading / Back Section. Omitted for a left-aligned title with nothing\n before it, so the title lines up with the page content; kept for a centred\n title, where the empty slot balances the actions. */}\n {hasLeadingSlot && (\n <div\n className={buildClassName(\n 'flex items-center gap-1 shrink-0 z-10',\n // A Back button is its own 40px target; reserve the width only for other content.\n !onBack && 'min-w-[40px]',\n )}\n data-testid=\"mobile-header-leading\"\n >\n {onBack ? (\n <button\n type=\"button\"\n onClick={onBack}\n aria-label={backLabel}\n // 40px-tall touch target hanging into the row padding: the chevron\n // sits on the content edge, with no right padding, so the title\n // follows ~10px after it.\n className=\"flex min-h-10 min-w-8 items-center justify-start gap-0.5 pl-2.5 pr-0 sm:pr-2 -ml-2.5 text-blue-600 dark:text-blue-400 hover:text-blue-700 rounded-xl transition-colors cursor-pointer\"\n data-testid=\"mobile-header-back-btn\"\n >\n <ChevronLeftIcon />\n <span className=\"text-xs font-semibold hidden sm:inline\">{backLabel}</span>\n </button>\n ) : (\n leading\n )}\n </div>\n )}\n\n {/* Title & Subtitle Section */}\n <div\n className={buildClassName(\n 'flex-1 min-w-0',\n // The row's gap already separates a left title from a leading element;\n // a centred title keeps even padding.\n titleAlign === 'center' ? 'px-2' : 'pr-2',\n titleAlign === 'center'\n ? 'absolute inset-x-12 text-center pointer-events-none flex flex-col items-center justify-center'\n : 'text-left',\n )}\n >\n {title && (\n <div\n className=\"text-sm font-bold text-gray-900 dark:text-white truncate pointer-events-auto\"\n data-testid=\"mobile-header-title\"\n >\n {title}\n </div>\n )}\n {subtitle && (\n <div\n className=\"text-[11px] text-gray-500 dark:text-gray-400 truncate pointer-events-auto\"\n data-testid=\"mobile-header-subtitle\"\n >\n {subtitle}\n </div>\n )}\n </div>\n\n {/* Right Actions Section */}\n <div className=\"flex items-center gap-1 min-w-[40px] shrink-0 justify-end z-10\">\n {searchable && (\n <button\n type=\"button\"\n onClick={() => setIsSearchActive(true)}\n aria-label=\"Search\"\n className=\"p-2 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-xl transition-colors cursor-pointer\"\n data-testid=\"mobile-header-search-toggle\"\n >\n <SearchIcon />\n </button>\n )}\n\n {visibleActions.map((action: MobileHeaderAction) => {\n const badgeColor = {\n danger: 'bg-red-500 text-white',\n info: 'bg-blue-500 text-white',\n success: 'bg-emerald-500 text-white',\n }[action.badgeVariant || 'danger']\n\n return (\n <button\n key={action.id}\n type=\"button\"\n disabled={action.disabled}\n onClick={action.onClick}\n aria-label={action.label}\n className={buildClassName(\n 'p-2 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-xl transition-colors relative cursor-pointer',\n action.disabled && 'opacity-40 cursor-not-allowed pointer-events-none',\n )}\n data-testid={`mobile-header-action-${action.id}`}\n >\n <span className=\"text-base\">{action.icon}</span>\n {action.badge !== undefined && action.badge !== null && (\n <span\n className={buildClassName(\n 'absolute top-1 right-1 min-w-[14px] h-3.5 px-0.5 rounded-full text-[9px] font-bold flex items-center justify-center shadow-xs',\n badgeColor,\n )}\n data-testid={`mobile-header-badge-${action.id}`}\n >\n {action.badge}\n </span>\n )}\n </button>\n )\n })}\n\n {overflowActions.length > 0 && (\n <button\n type=\"button\"\n onClick={() => setIsOverflowOpen(true)}\n aria-label={overflowLabel}\n aria-haspopup=\"dialog\"\n aria-expanded={isOverflowOpen}\n className=\"p-2 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-xl transition-colors cursor-pointer\"\n data-testid=\"mobile-header-overflow\"\n >\n <MoreIcon />\n </button>\n )}\n\n {trailing}\n </div>\n </>\n )}\n </div>\n\n {overflowActions.length > 0 && (\n <ActionSheet\n isOpen={isOverflowOpen}\n onClose={() => setIsOverflowOpen(false)}\n actions={overflowActions.map((action) => ({\n id: action.id,\n label: action.label,\n icon: action.icon,\n disabled: action.disabled,\n onClick: action.onClick,\n }))}\n />\n )}\n\n {/* Optional Bottom Sub-Header Slot (e.g. Category tabs or filter bar) */}\n {bottomSlot && (\n <div\n className=\"px-3 py-2 border-t border-[var(--ui-border-muted)]\"\n data-testid=\"mobile-header-bottom-slot\"\n >\n {bottomSlot}\n </div>\n )}\n </header>\n )\n}\n\nMobileHeader.displayName = 'MobileHeader'\n"],"names":["_jsx","_jsxs","useState","buildClassName","_Fragment","ActionSheet"],"mappings":"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"}
|
|
@@ -86,7 +86,9 @@ const Toast = ({ id, title, message, type, variant = 'accent', action, icon, sho
|
|
|
86
86
|
const enterFrom = isTop ? 'opacity-0 -translate-y-3 scale-95' : 'opacity-0 translate-y-3 scale-95';
|
|
87
87
|
const leaveTo = isTop ? 'opacity-0 -translate-y-2 scale-95' : 'opacity-0 translate-y-2 scale-95';
|
|
88
88
|
const renderedIcon = icon !== null && icon !== void 0 ? icon : (variant === 'filled' ? filledIconMap[type] : iconMap[type]);
|
|
89
|
-
return (jsxRuntime.jsx(react.Transition, { appear: true, show: true, enter: "transform transition duration-300 ease-out", enterFrom: enterFrom, enterTo: "opacity-100 translate-y-0 scale-100", leave: "transition duration-200 ease-in", leaveFrom: "opacity-100 translate-y-0 scale-100", leaveTo: leaveTo, children: jsxRuntime.jsxs("div", { role: "status", id: id.toString(), className: buildClassname.buildClassName(
|
|
89
|
+
return (jsxRuntime.jsx(react.Transition, { appear: true, show: true, enter: "transform transition duration-300 ease-out", enterFrom: enterFrom, enterTo: "opacity-100 translate-y-0 scale-100", leave: "transition duration-200 ease-in", leaveFrom: "opacity-100 translate-y-0 scale-100", leaveTo: leaveTo, children: jsxRuntime.jsxs("div", { role: "status", id: id.toString(), className: buildClassname.buildClassName(
|
|
90
|
+
// Never wider than the viewport minus a 16px gutter each side.
|
|
91
|
+
'relative overflow-hidden flex items-start gap-3 rounded-xl px-4 py-3.5 min-w-[min(280px,calc(100vw-2rem))] max-w-[min(28rem,calc(100vw-2rem))] transition-all', variantStyleMap[variant][type], className), children: [jsxRuntime.jsx("div", { className: "shrink-0 mt-0.5", children: variant === 'filled' ? (renderedIcon) : (jsxRuntime.jsx(TextContent.TextContent, { className: buildClassname.buildClassName(textColorMap[type]), children: renderedIcon })) }), jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col gap-0.5", children: [title && (jsxRuntime.jsx("div", { className: buildClassname.buildClassName('font-semibold text-sm leading-snug', variant === 'filled' ? 'text-white' : 'text-gray-900 dark:text-gray-100'), children: title })), jsxRuntime.jsx("div", { className: buildClassname.buildClassName('text-sm font-medium leading-relaxed', variant === 'filled' ? 'text-white/95' : 'text-gray-700 dark:text-gray-300'), children: message }), action && (jsxRuntime.jsx("div", { className: "mt-1.5", children: jsxRuntime.jsx("button", { type: "button", onClick: action.onClick, className: buildClassname.buildClassName('text-xs font-semibold underline underline-offset-2 transition-opacity focus:outline-none', variant === 'filled'
|
|
90
92
|
? 'text-white hover:opacity-85'
|
|
91
93
|
: 'text-[var(--ui-primary)] hover:opacity-80'), children: action.label }) }))] }), jsxRuntime.jsx(Button.Button, { onClick: onClose, "aria-label": "Close toast", className: buildClassname.buildClassName('shrink-0 mt-0.5 opacity-60 hover:opacity-100 focus-visible:opacity-100 transition', variant === 'filled' && 'text-white hover:text-white focus-visible:outline-current'), iconOnly: true, size: "xs", variant: "plain", theme: variant === 'filled' ? 'primary' : 'secondary', children: jsxRuntime.jsx(X.default, { className: "size-4" }) }), showProgressBar && duration > 0 && (jsxRuntime.jsx("div", { className: "absolute bottom-0 left-0 right-0 h-1 bg-black/10 dark:bg-white/10 overflow-hidden", children: jsxRuntime.jsx("div", { className: buildClassname.buildClassName('h-full transition-all duration-75 ease-linear', variant === 'filled' ? 'bg-white/70' : progressBarColorMap[type]), style: { width: `${Math.max(0, progress)}%` } }) }))] }) }));
|
|
92
94
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Toast.js","sources":["../../../../../src/components/Toast/Toast.tsx"],"sourcesContent":["import React, { useEffect, useState } from 'react'\nimport { Transition } from '@headlessui/react'\nimport XIcon from '../Icons/X.svg'\nimport CheckCircle from '../Icons/CircleCheck.svg'\nimport AlertCircle from '../Icons/CircleAlert.svg'\nimport Info from '../Icons/Info.svg'\nimport AlertTriangle from '../Icons/TriangleAlert.svg'\n\nimport { Button } from '../Button'\nimport { TextContent } from '../TextContent'\nimport { buildClassName } from '../../utils/build-classname'\n\nimport type { ToastProps, ToastType, ToastVariant } from './Toast.types'\n\n/**\n * Icons for each toast type\n */\nconst iconMap: Record<ToastType, React.ReactNode> = {\n success: <CheckCircle className=\"size-5 text-[var(--ui-success)]\" />,\n error: <AlertCircle className=\"size-5 text-[var(--ui-danger)]\" />,\n info: <Info className=\"size-5 text-[var(--ui-info)]\" />,\n warning: <AlertTriangle className=\"size-5 text-[var(--ui-warning)]\" />,\n}\n\nconst filledIconMap: Record<ToastType, React.ReactNode> = {\n success: <CheckCircle className=\"size-5 text-white\" />,\n error: <AlertCircle className=\"size-5 text-white\" />,\n info: <Info className=\"size-5 text-white\" />,\n warning: <AlertTriangle className=\"size-5 text-white\" />,\n}\n\nconst textColorMap: Record<ToastType, string> = {\n success: 'text-[var(--ui-success-text)]',\n error: 'text-[var(--ui-danger-text)]',\n info: 'text-[var(--ui-info-text)]',\n warning: 'text-[var(--ui-warning-text)]',\n}\n\nconst progressBarColorMap: Record<ToastType, string> = {\n success: 'bg-[var(--ui-success)]',\n error: 'bg-[var(--ui-danger)]',\n info: 'bg-[var(--ui-info)]',\n warning: 'bg-[var(--ui-warning)]',\n}\n\nconst variantStyleMap: Record<ToastVariant, Record<ToastType, string>> = {\n accent: {\n success:\n 'bg-white dark:bg-gray-900 border-l-4 border-[var(--ui-success)] text-gray-900 dark:text-gray-100 shadow-lg border-y border-r border-[var(--ui-border)]',\n error:\n 'bg-white dark:bg-gray-900 border-l-4 border-[var(--ui-danger)] text-gray-900 dark:text-gray-100 shadow-lg border-y border-r border-[var(--ui-border)]',\n info: 'bg-white dark:bg-gray-900 border-l-4 border-[var(--ui-info)] text-gray-900 dark:text-gray-100 shadow-lg border-y border-r border-[var(--ui-border)]',\n warning:\n 'bg-white dark:bg-gray-900 border-l-4 border-[var(--ui-warning)] text-gray-900 dark:text-gray-100 shadow-lg border-y border-r border-[var(--ui-border)]',\n },\n filled: {\n success: 'bg-[var(--ui-success)] text-white shadow-lg',\n error: 'bg-[var(--ui-danger)] text-white shadow-lg',\n info: 'bg-[var(--ui-info)] text-white shadow-lg',\n warning: 'bg-[var(--ui-warning)] text-white shadow-lg',\n },\n outlined: {\n success:\n 'bg-white dark:bg-gray-900 border-2 border-[var(--ui-success)] text-gray-900 dark:text-gray-100 shadow-md',\n error:\n 'bg-white dark:bg-gray-900 border-2 border-[var(--ui-danger)] text-gray-900 dark:text-gray-100 shadow-md',\n info: 'bg-white dark:bg-gray-900 border-2 border-[var(--ui-info)] text-gray-900 dark:text-gray-100 shadow-md',\n warning:\n 'bg-white dark:bg-gray-900 border-2 border-[var(--ui-warning)] text-gray-900 dark:text-gray-100 shadow-md',\n },\n glass: {\n success:\n 'backdrop-blur-md bg-white/85 dark:bg-gray-900/85 border border-emerald-500/30 text-gray-900 dark:text-gray-100 shadow-xl',\n error:\n 'backdrop-blur-md bg-white/85 dark:bg-gray-900/85 border border-rose-500/30 text-gray-900 dark:text-gray-100 shadow-xl',\n info: 'backdrop-blur-md bg-white/85 dark:bg-gray-900/85 border border-blue-500/30 text-gray-900 dark:text-gray-100 shadow-xl',\n warning:\n 'backdrop-blur-md bg-white/85 dark:bg-gray-900/85 border border-amber-500/30 text-gray-900 dark:text-gray-100 shadow-xl',\n },\n}\n\nexport const Toast: React.FC<ToastProps & { isTop?: boolean }> = ({\n id,\n title,\n message,\n type,\n variant = 'accent',\n action,\n icon,\n showProgressBar = true,\n duration = 4000,\n isPaused = false,\n onClose,\n className,\n isTop = true,\n}) => {\n const [progress, setProgress] = useState(100)\n\n useEffect(() => {\n if (!showProgressBar || !duration || isPaused) return\n\n const intervalTime = 50\n const step = (intervalTime / duration) * 100\n\n const timer = setInterval(() => {\n setProgress((prev) => {\n if (prev <= 0) {\n clearInterval(timer)\n return 0\n }\n return prev - step\n })\n }, intervalTime)\n\n return () => clearInterval(timer)\n }, [showProgressBar, duration, isPaused])\n\n const enterFrom = isTop ? 'opacity-0 -translate-y-3 scale-95' : 'opacity-0 translate-y-3 scale-95'\n const leaveTo = isTop ? 'opacity-0 -translate-y-2 scale-95' : 'opacity-0 translate-y-2 scale-95'\n\n const renderedIcon = icon ?? (variant === 'filled' ? filledIconMap[type] : iconMap[type])\n\n return (\n <Transition\n appear\n show\n enter=\"transform transition duration-300 ease-out\"\n enterFrom={enterFrom}\n enterTo=\"opacity-100 translate-y-0 scale-100\"\n leave=\"transition duration-200 ease-in\"\n leaveFrom=\"opacity-100 translate-y-0 scale-100\"\n leaveTo={leaveTo}\n >\n <div\n role=\"status\"\n id={id.toString()}\n className={buildClassName(\n 'relative overflow-hidden flex items-start gap-3 rounded-xl px-4 py-3.5 min-w-[280px] max-w-md transition-all',\n variantStyleMap[variant][type],\n className,\n )}\n >\n {/* Icon */}\n <div className=\"shrink-0 mt-0.5\">\n {variant === 'filled' ? (\n renderedIcon\n ) : (\n <TextContent className={buildClassName(textColorMap[type])}>{renderedIcon}</TextContent>\n )}\n </div>\n\n {/* Content Body */}\n <div className=\"flex-1 flex flex-col gap-0.5\">\n {title && (\n <div\n className={buildClassName(\n 'font-semibold text-sm leading-snug',\n variant === 'filled' ? 'text-white' : 'text-gray-900 dark:text-gray-100',\n )}\n >\n {title}\n </div>\n )}\n <div\n className={buildClassName(\n 'text-sm font-medium leading-relaxed',\n variant === 'filled' ? 'text-white/95' : 'text-gray-700 dark:text-gray-300',\n )}\n >\n {message}\n </div>\n\n {/* Action Button */}\n {action && (\n <div className=\"mt-1.5\">\n <button\n type=\"button\"\n onClick={action.onClick}\n className={buildClassName(\n 'text-xs font-semibold underline underline-offset-2 transition-opacity focus:outline-none',\n variant === 'filled'\n ? 'text-white hover:opacity-85'\n : 'text-[var(--ui-primary)] hover:opacity-80',\n )}\n >\n {action.label}\n </button>\n </div>\n )}\n </div>\n\n {/* Close button */}\n <Button\n onClick={onClose}\n aria-label=\"Close toast\"\n className={buildClassName(\n 'shrink-0 mt-0.5 opacity-60 hover:opacity-100 focus-visible:opacity-100 transition',\n variant === 'filled' && 'text-white hover:text-white focus-visible:outline-current',\n )}\n iconOnly\n size=\"xs\"\n variant=\"plain\"\n theme={variant === 'filled' ? 'primary' : 'secondary'}\n >\n <XIcon className=\"size-4\" />\n </Button>\n\n {/* Progress Bar Countdown */}\n {showProgressBar && duration > 0 && (\n <div className=\"absolute bottom-0 left-0 right-0 h-1 bg-black/10 dark:bg-white/10 overflow-hidden\">\n <div\n className={buildClassName(\n 'h-full transition-all duration-75 ease-linear',\n variant === 'filled' ? 'bg-white/70' : progressBarColorMap[type],\n )}\n style={{ width: `${Math.max(0, progress)}%` }}\n />\n </div>\n )}\n </div>\n </Transition>\n )\n}\n\nToast.displayName = 'Toast'\n"],"names":["_jsx","CheckCircle","AlertCircle","Info","AlertTriangle","useState","useEffect","Transition","_jsxs","buildClassName","TextContent","Button","XIcon"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Toast.js","sources":["../../../../../src/components/Toast/Toast.tsx"],"sourcesContent":["import React, { useEffect, useState } from 'react'\nimport { Transition } from '@headlessui/react'\nimport XIcon from '../Icons/X.svg'\nimport CheckCircle from '../Icons/CircleCheck.svg'\nimport AlertCircle from '../Icons/CircleAlert.svg'\nimport Info from '../Icons/Info.svg'\nimport AlertTriangle from '../Icons/TriangleAlert.svg'\n\nimport { Button } from '../Button'\nimport { TextContent } from '../TextContent'\nimport { buildClassName } from '../../utils/build-classname'\n\nimport type { ToastProps, ToastType, ToastVariant } from './Toast.types'\n\n/**\n * Icons for each toast type\n */\nconst iconMap: Record<ToastType, React.ReactNode> = {\n success: <CheckCircle className=\"size-5 text-[var(--ui-success)]\" />,\n error: <AlertCircle className=\"size-5 text-[var(--ui-danger)]\" />,\n info: <Info className=\"size-5 text-[var(--ui-info)]\" />,\n warning: <AlertTriangle className=\"size-5 text-[var(--ui-warning)]\" />,\n}\n\nconst filledIconMap: Record<ToastType, React.ReactNode> = {\n success: <CheckCircle className=\"size-5 text-white\" />,\n error: <AlertCircle className=\"size-5 text-white\" />,\n info: <Info className=\"size-5 text-white\" />,\n warning: <AlertTriangle className=\"size-5 text-white\" />,\n}\n\nconst textColorMap: Record<ToastType, string> = {\n success: 'text-[var(--ui-success-text)]',\n error: 'text-[var(--ui-danger-text)]',\n info: 'text-[var(--ui-info-text)]',\n warning: 'text-[var(--ui-warning-text)]',\n}\n\nconst progressBarColorMap: Record<ToastType, string> = {\n success: 'bg-[var(--ui-success)]',\n error: 'bg-[var(--ui-danger)]',\n info: 'bg-[var(--ui-info)]',\n warning: 'bg-[var(--ui-warning)]',\n}\n\nconst variantStyleMap: Record<ToastVariant, Record<ToastType, string>> = {\n accent: {\n success:\n 'bg-white dark:bg-gray-900 border-l-4 border-[var(--ui-success)] text-gray-900 dark:text-gray-100 shadow-lg border-y border-r border-[var(--ui-border)]',\n error:\n 'bg-white dark:bg-gray-900 border-l-4 border-[var(--ui-danger)] text-gray-900 dark:text-gray-100 shadow-lg border-y border-r border-[var(--ui-border)]',\n info: 'bg-white dark:bg-gray-900 border-l-4 border-[var(--ui-info)] text-gray-900 dark:text-gray-100 shadow-lg border-y border-r border-[var(--ui-border)]',\n warning:\n 'bg-white dark:bg-gray-900 border-l-4 border-[var(--ui-warning)] text-gray-900 dark:text-gray-100 shadow-lg border-y border-r border-[var(--ui-border)]',\n },\n filled: {\n success: 'bg-[var(--ui-success)] text-white shadow-lg',\n error: 'bg-[var(--ui-danger)] text-white shadow-lg',\n info: 'bg-[var(--ui-info)] text-white shadow-lg',\n warning: 'bg-[var(--ui-warning)] text-white shadow-lg',\n },\n outlined: {\n success:\n 'bg-white dark:bg-gray-900 border-2 border-[var(--ui-success)] text-gray-900 dark:text-gray-100 shadow-md',\n error:\n 'bg-white dark:bg-gray-900 border-2 border-[var(--ui-danger)] text-gray-900 dark:text-gray-100 shadow-md',\n info: 'bg-white dark:bg-gray-900 border-2 border-[var(--ui-info)] text-gray-900 dark:text-gray-100 shadow-md',\n warning:\n 'bg-white dark:bg-gray-900 border-2 border-[var(--ui-warning)] text-gray-900 dark:text-gray-100 shadow-md',\n },\n glass: {\n success:\n 'backdrop-blur-md bg-white/85 dark:bg-gray-900/85 border border-emerald-500/30 text-gray-900 dark:text-gray-100 shadow-xl',\n error:\n 'backdrop-blur-md bg-white/85 dark:bg-gray-900/85 border border-rose-500/30 text-gray-900 dark:text-gray-100 shadow-xl',\n info: 'backdrop-blur-md bg-white/85 dark:bg-gray-900/85 border border-blue-500/30 text-gray-900 dark:text-gray-100 shadow-xl',\n warning:\n 'backdrop-blur-md bg-white/85 dark:bg-gray-900/85 border border-amber-500/30 text-gray-900 dark:text-gray-100 shadow-xl',\n },\n}\n\nexport const Toast: React.FC<ToastProps & { isTop?: boolean }> = ({\n id,\n title,\n message,\n type,\n variant = 'accent',\n action,\n icon,\n showProgressBar = true,\n duration = 4000,\n isPaused = false,\n onClose,\n className,\n isTop = true,\n}) => {\n const [progress, setProgress] = useState(100)\n\n useEffect(() => {\n if (!showProgressBar || !duration || isPaused) return\n\n const intervalTime = 50\n const step = (intervalTime / duration) * 100\n\n const timer = setInterval(() => {\n setProgress((prev) => {\n if (prev <= 0) {\n clearInterval(timer)\n return 0\n }\n return prev - step\n })\n }, intervalTime)\n\n return () => clearInterval(timer)\n }, [showProgressBar, duration, isPaused])\n\n const enterFrom = isTop ? 'opacity-0 -translate-y-3 scale-95' : 'opacity-0 translate-y-3 scale-95'\n const leaveTo = isTop ? 'opacity-0 -translate-y-2 scale-95' : 'opacity-0 translate-y-2 scale-95'\n\n const renderedIcon = icon ?? (variant === 'filled' ? filledIconMap[type] : iconMap[type])\n\n return (\n <Transition\n appear\n show\n enter=\"transform transition duration-300 ease-out\"\n enterFrom={enterFrom}\n enterTo=\"opacity-100 translate-y-0 scale-100\"\n leave=\"transition duration-200 ease-in\"\n leaveFrom=\"opacity-100 translate-y-0 scale-100\"\n leaveTo={leaveTo}\n >\n <div\n role=\"status\"\n id={id.toString()}\n className={buildClassName(\n // Never wider than the viewport minus a 16px gutter each side.\n 'relative overflow-hidden flex items-start gap-3 rounded-xl px-4 py-3.5 min-w-[min(280px,calc(100vw-2rem))] max-w-[min(28rem,calc(100vw-2rem))] transition-all',\n variantStyleMap[variant][type],\n className,\n )}\n >\n {/* Icon */}\n <div className=\"shrink-0 mt-0.5\">\n {variant === 'filled' ? (\n renderedIcon\n ) : (\n <TextContent className={buildClassName(textColorMap[type])}>{renderedIcon}</TextContent>\n )}\n </div>\n\n {/* Content Body */}\n <div className=\"flex-1 flex flex-col gap-0.5\">\n {title && (\n <div\n className={buildClassName(\n 'font-semibold text-sm leading-snug',\n variant === 'filled' ? 'text-white' : 'text-gray-900 dark:text-gray-100',\n )}\n >\n {title}\n </div>\n )}\n <div\n className={buildClassName(\n 'text-sm font-medium leading-relaxed',\n variant === 'filled' ? 'text-white/95' : 'text-gray-700 dark:text-gray-300',\n )}\n >\n {message}\n </div>\n\n {/* Action Button */}\n {action && (\n <div className=\"mt-1.5\">\n <button\n type=\"button\"\n onClick={action.onClick}\n className={buildClassName(\n 'text-xs font-semibold underline underline-offset-2 transition-opacity focus:outline-none',\n variant === 'filled'\n ? 'text-white hover:opacity-85'\n : 'text-[var(--ui-primary)] hover:opacity-80',\n )}\n >\n {action.label}\n </button>\n </div>\n )}\n </div>\n\n {/* Close button */}\n <Button\n onClick={onClose}\n aria-label=\"Close toast\"\n className={buildClassName(\n 'shrink-0 mt-0.5 opacity-60 hover:opacity-100 focus-visible:opacity-100 transition',\n variant === 'filled' && 'text-white hover:text-white focus-visible:outline-current',\n )}\n iconOnly\n size=\"xs\"\n variant=\"plain\"\n theme={variant === 'filled' ? 'primary' : 'secondary'}\n >\n <XIcon className=\"size-4\" />\n </Button>\n\n {/* Progress Bar Countdown */}\n {showProgressBar && duration > 0 && (\n <div className=\"absolute bottom-0 left-0 right-0 h-1 bg-black/10 dark:bg-white/10 overflow-hidden\">\n <div\n className={buildClassName(\n 'h-full transition-all duration-75 ease-linear',\n variant === 'filled' ? 'bg-white/70' : progressBarColorMap[type],\n )}\n style={{ width: `${Math.max(0, progress)}%` }}\n />\n </div>\n )}\n </div>\n </Transition>\n )\n}\n\nToast.displayName = 'Toast'\n"],"names":["_jsx","CheckCircle","AlertCircle","Info","AlertTriangle","useState","useEffect","Transition","_jsxs","buildClassName","TextContent","Button","XIcon"],"mappings":"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"}
|
|
@@ -4,6 +4,7 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
|
4
4
|
var React = require('react');
|
|
5
5
|
var Toast = require('./Toast.js');
|
|
6
6
|
var buildClassname = require('../../utils/build-classname.js');
|
|
7
|
+
var useBreakpoint = require('../../hooks/use-breakpoint.js');
|
|
7
8
|
|
|
8
9
|
const ToastContext = React.createContext(undefined);
|
|
9
10
|
let toastCounter = 0;
|
|
@@ -17,11 +18,15 @@ const placementClass = {
|
|
|
17
18
|
'top-right': 'top-20 right-6 items-end',
|
|
18
19
|
'top-left': 'top-20 left-6 items-start',
|
|
19
20
|
'top-center': 'top-20 left-1/2 -translate-x-1/2 items-center',
|
|
20
|
-
|
|
21
|
-
'bottom-
|
|
22
|
-
'bottom-
|
|
21
|
+
// Bottom placements clear the home indicator on notched phones (0 elsewhere).
|
|
22
|
+
'bottom-right': 'bottom-[calc(1.5rem+env(safe-area-inset-bottom,0px))] right-6 items-end',
|
|
23
|
+
'bottom-left': 'bottom-[calc(1.5rem+env(safe-area-inset-bottom,0px))] left-6 items-start',
|
|
24
|
+
'bottom-center': 'bottom-[calc(1.5rem+env(safe-area-inset-bottom,0px))] left-1/2 -translate-x-1/2 items-center',
|
|
23
25
|
};
|
|
24
|
-
const ToastProvider = ({ children, placement = 'top-right', maxToasts = 5, className, }) => {
|
|
26
|
+
const ToastProvider = ({ children, placement: placementProp = 'top-right', maxToasts = 5, offset, className, }) => {
|
|
27
|
+
const placement = useBreakpoint.useResponsiveValue(placementProp);
|
|
28
|
+
const isTop = placement.startsWith('top');
|
|
29
|
+
const edgeOffset = isTop ? offset === null || offset === void 0 ? void 0 : offset.top : offset === null || offset === void 0 ? void 0 : offset.bottom;
|
|
25
30
|
const [toasts, setToasts] = React.useState([]);
|
|
26
31
|
const [pausedIds, setPausedIds] = React.useState(new Set());
|
|
27
32
|
const metaRefs = React.useRef(new Map());
|
|
@@ -142,7 +147,7 @@ const ToastProvider = ({ children, placement = 'top-right', maxToasts = 5, class
|
|
|
142
147
|
dismiss: (id) => closeToast(id),
|
|
143
148
|
dismissAll: () => dismissAll(),
|
|
144
149
|
}), [showToast, closeToast, dismissAll]);
|
|
145
|
-
return (jsxRuntime.jsxs(ToastContext.Provider, { value: { showToast, closeToast, toast: toastHelpers }, children: [children, jsxRuntime.jsx("div", { className: buildClassname.buildClassName('fixed z-[9999] flex flex-col gap-3 pointer-events-none', placementClass[placement], className), children: toasts.map((toastItem) => (jsxRuntime.jsx("div", { className: "pointer-events-auto", onMouseEnter: () => handleMouseEnter(toastItem), onMouseLeave: () => handleMouseLeave(toastItem), children: jsxRuntime.jsx(Toast.Toast, { ...toastItem, isPaused: pausedIds.has(toastItem.id), isTop:
|
|
150
|
+
return (jsxRuntime.jsxs(ToastContext.Provider, { value: { showToast, closeToast, toast: toastHelpers }, children: [children, jsxRuntime.jsx("div", { className: buildClassname.buildClassName('fixed z-[9999] flex flex-col gap-3 pointer-events-none', placementClass[placement], className), style: edgeOffset ? (isTop ? { top: edgeOffset } : { bottom: edgeOffset }) : undefined, "data-testid": "toast-container", "data-placement": placement, children: toasts.map((toastItem) => (jsxRuntime.jsx("div", { className: "pointer-events-auto", onMouseEnter: () => handleMouseEnter(toastItem), onMouseLeave: () => handleMouseLeave(toastItem), children: jsxRuntime.jsx(Toast.Toast, { ...toastItem, isPaused: pausedIds.has(toastItem.id), isTop: isTop, onClose: () => closeToast(toastItem.id) }) }, toastItem.id))) })] }));
|
|
146
151
|
};
|
|
147
152
|
ToastProvider.displayName = 'ToastProvider';
|
|
148
153
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToastProvider.js","sources":["../../../../../src/components/Toast/ToastProvider.tsx"],"sourcesContent":["import React, {\n createContext,\n useContext,\n useEffect,\n useState,\n type ReactNode,\n useRef,\n useCallback,\n useMemo,\n} from 'react'\nimport { Toast } from './Toast'\nimport type {\n IToast,\n ToastContextType,\n ToastOptions,\n ToastPlacement,\n ToastProviderProps,\n} from './Toast.types'\nimport { buildClassName } from '../../utils/build-classname'\n\nconst ToastContext = createContext<ToastContextType | undefined>(undefined)\nlet toastCounter = 0\n\nexport const useToast = (): ToastContextType => {\n const context = useContext(ToastContext)\n if (!context) throw new Error('useToast must be used within ToastProvider')\n return context\n}\n\nconst placementClass: Record<ToastPlacement, string> = {\n 'top-right': 'top-20 right-6 items-end',\n 'top-left': 'top-20 left-6 items-start',\n 'top-center': 'top-20 left-1/2 -translate-x-1/2 items-center',\n\n 'bottom-right': 'bottom-6 right-6 items-end',\n 'bottom-left': 'bottom-6 left-6 items-start',\n 'bottom-center': 'bottom-6 left-1/2 -translate-x-1/2 items-center',\n}\n\ninterface ToastMeta {\n timerId: any\n startTime: number\n remainingTime: number\n}\n\nexport const ToastProvider: React.FC<ToastProviderProps> = ({\n children,\n placement = 'top-right',\n maxToasts = 5,\n className,\n}) => {\n const [toasts, setToasts] = useState<IToast[]>([])\n const [pausedIds, setPausedIds] = useState<Set<number>>(new Set())\n const metaRefs = useRef<Map<number, ToastMeta>>(new Map())\n\n const clearTimer = (id: number) => {\n const meta = metaRefs.current.get(id)\n if (meta?.timerId) {\n clearTimeout(meta.timerId)\n meta.timerId = null\n }\n }\n\n const closeToast = useCallback((id: number) => {\n clearTimer(id)\n metaRefs.current.delete(id)\n setPausedIds((prev) => {\n const next = new Set(prev)\n next.delete(id)\n return next\n })\n setToasts((prev) => prev.filter((t) => t.id !== id))\n }, [])\n\n const dismissAll = useCallback(() => {\n metaRefs.current.forEach((meta) => {\n if (meta.timerId) clearTimeout(meta.timerId)\n })\n metaRefs.current.clear()\n setPausedIds(new Set())\n setToasts([])\n }, [])\n\n const startAutoClose = useCallback(\n (toastId: number, delay: number) => {\n clearTimer(toastId)\n\n const timerId = setTimeout(() => {\n closeToast(toastId)\n }, delay)\n\n const meta = metaRefs.current.get(toastId)\n if (meta) {\n meta.timerId = timerId\n meta.startTime = Date.now()\n meta.remainingTime = delay\n } else {\n metaRefs.current.set(toastId, {\n timerId,\n startTime: Date.now(),\n remainingTime: delay,\n })\n }\n },\n [closeToast],\n )\n\n useEffect(() => {\n const refs = metaRefs.current\n return () =>\n refs.forEach((meta) => {\n if (meta.timerId) clearTimeout(meta.timerId)\n })\n }, [])\n\n const showToast = useCallback(\n (message: ReactNode, options?: ToastOptions): number => {\n const id = ++toastCounter\n\n const newToast: IToast = {\n id,\n title: options?.title,\n message,\n type: options?.type ?? 'info',\n variant: options?.variant ?? 'accent',\n duration: options?.duration ?? 4000,\n autoClose: options?.autoClose ?? true,\n pauseOnHover: options?.pauseOnHover ?? true,\n showProgressBar: options?.showProgressBar ?? options?.autoClose !== false,\n action: options?.action,\n icon: options?.icon,\n }\n\n setToasts((prev) => {\n const updated = [...prev, newToast]\n if (updated.length > maxToasts) updated.shift()\n return updated\n })\n\n if (newToast.autoClose) {\n startAutoClose(id, newToast.duration)\n }\n\n return id\n },\n [maxToasts, startAutoClose],\n )\n\n // Hover pauses timer and tracks exact remaining milliseconds\n const handleMouseEnter = (toast: IToast) => {\n if (!toast.pauseOnHover || !toast.autoClose) return\n\n const meta = metaRefs.current.get(toast.id)\n if (meta) {\n const elapsed = Date.now() - meta.startTime\n meta.remainingTime = Math.max(0, meta.remainingTime - elapsed)\n clearTimer(toast.id)\n setPausedIds((prev) => new Set(prev).add(toast.id))\n }\n }\n\n // Mouse leave resumes timer with exact remaining milliseconds\n const handleMouseLeave = (toast: IToast) => {\n if (!toast.pauseOnHover || !toast.autoClose) return\n\n setPausedIds((prev) => {\n const next = new Set(prev)\n next.delete(toast.id)\n return next\n })\n\n const meta = metaRefs.current.get(toast.id)\n if (meta && meta.remainingTime > 0) {\n startAutoClose(toast.id, meta.remainingTime)\n } else if (meta && meta.remainingTime <= 0) {\n closeToast(toast.id)\n }\n }\n\n const toastHelpers = useMemo(\n () => ({\n show: (message: ReactNode, options?: ToastOptions) => showToast(message, options),\n success: (message: ReactNode, options?: ToastOptions) =>\n showToast(message, { ...options, type: 'success' }),\n error: (message: ReactNode, options?: ToastOptions) =>\n showToast(message, { ...options, type: 'error' }),\n info: (message: ReactNode, options?: ToastOptions) =>\n showToast(message, { ...options, type: 'info' }),\n warning: (message: ReactNode, options?: ToastOptions) =>\n showToast(message, { ...options, type: 'warning' }),\n dismiss: (id: number) => closeToast(id),\n dismissAll: () => dismissAll(),\n }),\n [showToast, closeToast, dismissAll],\n )\n\n return (\n <ToastContext.Provider value={{ showToast, closeToast, toast: toastHelpers }}>\n {children}\n\n {/* Toast Container */}\n <div\n className={buildClassName(\n 'fixed z-[9999] flex flex-col gap-3 pointer-events-none',\n placementClass[placement],\n className,\n )}\n >\n {toasts.map((toastItem) => (\n <div\n key={toastItem.id}\n className=\"pointer-events-auto\"\n onMouseEnter={() => handleMouseEnter(toastItem)}\n onMouseLeave={() => handleMouseLeave(toastItem)}\n >\n <Toast\n {...toastItem}\n isPaused={pausedIds.has(toastItem.id)}\n isTop={placement.startsWith('top')}\n onClose={() => closeToast(toastItem.id)}\n />\n </div>\n ))}\n </div>\n </ToastContext.Provider>\n )\n}\n\nToastProvider.displayName = 'ToastProvider'\n"],"names":["createContext","useContext","useState","useRef","useCallback","useEffect","useMemo","_jsxs","_jsx","buildClassName","Toast"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ToastProvider.js","sources":["../../../../../src/components/Toast/ToastProvider.tsx"],"sourcesContent":["import React, {\n createContext,\n useContext,\n useEffect,\n useState,\n type ReactNode,\n useRef,\n useCallback,\n useMemo,\n} from 'react'\nimport { Toast } from './Toast'\nimport type {\n IToast,\n ToastContextType,\n ToastOptions,\n ToastPlacement,\n ToastProviderProps,\n} from './Toast.types'\nimport { buildClassName } from '../../utils/build-classname'\nimport { useResponsiveValue } from '../../hooks/use-breakpoint'\n\nconst ToastContext = createContext<ToastContextType | undefined>(undefined)\nlet toastCounter = 0\n\nexport const useToast = (): ToastContextType => {\n const context = useContext(ToastContext)\n if (!context) throw new Error('useToast must be used within ToastProvider')\n return context\n}\n\nconst placementClass: Record<ToastPlacement, string> = {\n 'top-right': 'top-20 right-6 items-end',\n 'top-left': 'top-20 left-6 items-start',\n 'top-center': 'top-20 left-1/2 -translate-x-1/2 items-center',\n\n // Bottom placements clear the home indicator on notched phones (0 elsewhere).\n 'bottom-right': 'bottom-[calc(1.5rem+env(safe-area-inset-bottom,0px))] right-6 items-end',\n 'bottom-left': 'bottom-[calc(1.5rem+env(safe-area-inset-bottom,0px))] left-6 items-start',\n 'bottom-center':\n 'bottom-[calc(1.5rem+env(safe-area-inset-bottom,0px))] left-1/2 -translate-x-1/2 items-center',\n}\n\ninterface ToastMeta {\n timerId: any\n startTime: number\n remainingTime: number\n}\n\nexport const ToastProvider: React.FC<ToastProviderProps> = ({\n children,\n placement: placementProp = 'top-right',\n maxToasts = 5,\n offset,\n className,\n}) => {\n const placement = useResponsiveValue(placementProp)\n const isTop = placement.startsWith('top')\n const edgeOffset = isTop ? offset?.top : offset?.bottom\n const [toasts, setToasts] = useState<IToast[]>([])\n const [pausedIds, setPausedIds] = useState<Set<number>>(new Set())\n const metaRefs = useRef<Map<number, ToastMeta>>(new Map())\n\n const clearTimer = (id: number) => {\n const meta = metaRefs.current.get(id)\n if (meta?.timerId) {\n clearTimeout(meta.timerId)\n meta.timerId = null\n }\n }\n\n const closeToast = useCallback((id: number) => {\n clearTimer(id)\n metaRefs.current.delete(id)\n setPausedIds((prev) => {\n const next = new Set(prev)\n next.delete(id)\n return next\n })\n setToasts((prev) => prev.filter((t) => t.id !== id))\n }, [])\n\n const dismissAll = useCallback(() => {\n metaRefs.current.forEach((meta) => {\n if (meta.timerId) clearTimeout(meta.timerId)\n })\n metaRefs.current.clear()\n setPausedIds(new Set())\n setToasts([])\n }, [])\n\n const startAutoClose = useCallback(\n (toastId: number, delay: number) => {\n clearTimer(toastId)\n\n const timerId = setTimeout(() => {\n closeToast(toastId)\n }, delay)\n\n const meta = metaRefs.current.get(toastId)\n if (meta) {\n meta.timerId = timerId\n meta.startTime = Date.now()\n meta.remainingTime = delay\n } else {\n metaRefs.current.set(toastId, {\n timerId,\n startTime: Date.now(),\n remainingTime: delay,\n })\n }\n },\n [closeToast],\n )\n\n useEffect(() => {\n const refs = metaRefs.current\n return () =>\n refs.forEach((meta) => {\n if (meta.timerId) clearTimeout(meta.timerId)\n })\n }, [])\n\n const showToast = useCallback(\n (message: ReactNode, options?: ToastOptions): number => {\n const id = ++toastCounter\n\n const newToast: IToast = {\n id,\n title: options?.title,\n message,\n type: options?.type ?? 'info',\n variant: options?.variant ?? 'accent',\n duration: options?.duration ?? 4000,\n autoClose: options?.autoClose ?? true,\n pauseOnHover: options?.pauseOnHover ?? true,\n showProgressBar: options?.showProgressBar ?? options?.autoClose !== false,\n action: options?.action,\n icon: options?.icon,\n }\n\n setToasts((prev) => {\n const updated = [...prev, newToast]\n if (updated.length > maxToasts) updated.shift()\n return updated\n })\n\n if (newToast.autoClose) {\n startAutoClose(id, newToast.duration)\n }\n\n return id\n },\n [maxToasts, startAutoClose],\n )\n\n // Hover pauses timer and tracks exact remaining milliseconds\n const handleMouseEnter = (toast: IToast) => {\n if (!toast.pauseOnHover || !toast.autoClose) return\n\n const meta = metaRefs.current.get(toast.id)\n if (meta) {\n const elapsed = Date.now() - meta.startTime\n meta.remainingTime = Math.max(0, meta.remainingTime - elapsed)\n clearTimer(toast.id)\n setPausedIds((prev) => new Set(prev).add(toast.id))\n }\n }\n\n // Mouse leave resumes timer with exact remaining milliseconds\n const handleMouseLeave = (toast: IToast) => {\n if (!toast.pauseOnHover || !toast.autoClose) return\n\n setPausedIds((prev) => {\n const next = new Set(prev)\n next.delete(toast.id)\n return next\n })\n\n const meta = metaRefs.current.get(toast.id)\n if (meta && meta.remainingTime > 0) {\n startAutoClose(toast.id, meta.remainingTime)\n } else if (meta && meta.remainingTime <= 0) {\n closeToast(toast.id)\n }\n }\n\n const toastHelpers = useMemo(\n () => ({\n show: (message: ReactNode, options?: ToastOptions) => showToast(message, options),\n success: (message: ReactNode, options?: ToastOptions) =>\n showToast(message, { ...options, type: 'success' }),\n error: (message: ReactNode, options?: ToastOptions) =>\n showToast(message, { ...options, type: 'error' }),\n info: (message: ReactNode, options?: ToastOptions) =>\n showToast(message, { ...options, type: 'info' }),\n warning: (message: ReactNode, options?: ToastOptions) =>\n showToast(message, { ...options, type: 'warning' }),\n dismiss: (id: number) => closeToast(id),\n dismissAll: () => dismissAll(),\n }),\n [showToast, closeToast, dismissAll],\n )\n\n return (\n <ToastContext.Provider value={{ showToast, closeToast, toast: toastHelpers }}>\n {children}\n\n {/* Toast Container */}\n <div\n className={buildClassName(\n 'fixed z-[9999] flex flex-col gap-3 pointer-events-none',\n placementClass[placement],\n className,\n )}\n style={edgeOffset ? (isTop ? { top: edgeOffset } : { bottom: edgeOffset }) : undefined}\n data-testid=\"toast-container\"\n data-placement={placement}\n >\n {toasts.map((toastItem) => (\n <div\n key={toastItem.id}\n className=\"pointer-events-auto\"\n onMouseEnter={() => handleMouseEnter(toastItem)}\n onMouseLeave={() => handleMouseLeave(toastItem)}\n >\n <Toast\n {...toastItem}\n isPaused={pausedIds.has(toastItem.id)}\n isTop={isTop}\n onClose={() => closeToast(toastItem.id)}\n />\n </div>\n ))}\n </div>\n </ToastContext.Provider>\n )\n}\n\nToastProvider.displayName = 'ToastProvider'\n"],"names":["createContext","useContext","useResponsiveValue","useState","useRef","useCallback","useEffect","useMemo","_jsxs","_jsx","buildClassName","Toast"],"mappings":"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"}
|
|
@@ -39,12 +39,13 @@ function MobileHeader({ title, subtitle, titleAlign = 'center', onBack, backLabe
|
|
|
39
39
|
};
|
|
40
40
|
return (jsxs("header", { className: buildClassName('w-full transition-colors select-none z-30', sticky && 'sticky top-0', transparent
|
|
41
41
|
? 'bg-white/80 dark:bg-gray-900/80 backdrop-blur-md border-b border-[var(--ui-border-muted)]'
|
|
42
|
-
: 'bg-white dark:bg-gray-900 border-b border-[var(--ui-border)] shadow-xs', 'pt-[env(safe-area-inset-top,0px)]', className), "data-testid": "mobile-header", children: [jsx("div", { className: "h-14 px-4 flex items-center justify-between gap-
|
|
42
|
+
: 'bg-white dark:bg-gray-900 border-b border-[var(--ui-border)] shadow-xs', 'pt-[env(safe-area-inset-top,0px)]', className), "data-testid": "mobile-header", children: [jsx("div", { className: "h-14 px-4 flex items-center justify-between gap-2 relative", children: searchable && isSearchActive ? (jsxs("div", { className: "flex-1 flex items-center gap-2", "data-testid": "mobile-header-search-container", children: [jsxs("div", { className: "relative flex-1 flex items-center", children: [jsx("span", { className: "absolute left-3 pointer-events-none", children: jsx(SearchIcon, {}) }), jsx("input", { type: "text", value: searchValue, onChange: handleSearchInput, placeholder: searchPlaceholder, autoFocus: true, className: "w-full pl-9 pr-8 py-1.5 text-xs bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-white rounded-full border border-transparent focus:border-blue-500 focus:outline-hidden transition-all", "data-testid": "mobile-header-search-input" }), searchValue && (jsx("button", { type: "button", onClick: handleClearSearch, "aria-label": "Clear search", className: "absolute right-2.5 p-0.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 cursor-pointer", children: jsx(ClearIcon, {}) }))] }), jsx("button", { type: "button", onClick: () => setIsSearchActive(false), className: "text-xs font-semibold text-blue-600 dark:text-blue-400 px-2 py-1.5 rounded-lg hover:bg-blue-50 dark:hover:bg-blue-950/40 cursor-pointer", children: "Cancel" })] })) : (jsxs(Fragment, { children: [hasLeadingSlot && (jsx("div", { className: buildClassName('flex items-center gap-1 shrink-0 z-10',
|
|
43
43
|
// A Back button is its own 40px target; reserve the width only for other content.
|
|
44
44
|
!onBack && 'min-w-[40px]'), "data-testid": "mobile-header-leading", children: onBack ? (jsxs("button", { type: "button", onClick: onBack, "aria-label": backLabel,
|
|
45
|
-
// 40px touch target hanging into the row padding
|
|
46
|
-
//
|
|
47
|
-
|
|
45
|
+
// 40px-tall touch target hanging into the row padding: the chevron
|
|
46
|
+
// sits on the content edge, with no right padding, so the title
|
|
47
|
+
// follows ~10px after it.
|
|
48
|
+
className: "flex min-h-10 min-w-8 items-center justify-start gap-0.5 pl-2.5 pr-0 sm:pr-2 -ml-2.5 text-blue-600 dark:text-blue-400 hover:text-blue-700 rounded-xl transition-colors cursor-pointer", "data-testid": "mobile-header-back-btn", children: [jsx(ChevronLeftIcon, {}), jsx("span", { className: "text-xs font-semibold hidden sm:inline", children: backLabel })] })) : (leading) })), jsxs("div", { className: buildClassName('flex-1 min-w-0',
|
|
48
49
|
// The row's gap already separates a left title from a leading element;
|
|
49
50
|
// a centred title keeps even padding.
|
|
50
51
|
titleAlign === 'center' ? 'px-2' : 'pr-2', titleAlign === 'center'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MobileHeader.js","sources":["../../../../../src/components/MobileHeader/MobileHeader.tsx"],"sourcesContent":["import React, { useState } from 'react'\nimport type { MobileHeaderProps, MobileHeaderAction } from './MobileHeader.types'\nimport { buildClassName } from '../../utils/build-classname'\nimport { ActionSheet } from '../ActionSheet'\n\nfunction ChevronLeftIcon() {\n return (\n <svg className=\"size-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2.5} d=\"M15 19l-7-7 7-7\" />\n </svg>\n )\n}\n\nfunction SearchIcon() {\n return (\n <svg className=\"size-4 text-gray-400\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"\n />\n </svg>\n )\n}\n\nfunction MoreIcon() {\n return (\n <svg className=\"size-5\" fill=\"currentColor\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"5\" r=\"1.75\" />\n <circle cx=\"12\" cy=\"12\" r=\"1.75\" />\n <circle cx=\"12\" cy=\"19\" r=\"1.75\" />\n </svg>\n )\n}\n\nfunction ClearIcon() {\n return (\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n )\n}\n\nexport function MobileHeader({\n title,\n subtitle,\n titleAlign = 'center',\n onBack,\n backLabel = 'Back',\n leading,\n actions,\n maxVisibleActions,\n overflowLabel = 'More actions',\n trailing,\n searchable = false,\n searchValue: externalSearchValue,\n onSearchChange,\n searchPlaceholder = 'Search...',\n sticky = true,\n transparent = false,\n bottomSlot,\n className = '',\n}: MobileHeaderProps) {\n const [isSearchActive, setIsSearchActive] = useState(false)\n const [internalSearchValue, setInternalSearchValue] = useState('')\n const [isOverflowOpen, setIsOverflowOpen] = useState(false)\n const hasLeadingSlot = Boolean(onBack || leading) || titleAlign === 'center'\n\n // The overflow button takes one of the visible slots.\n const allActions = actions ?? []\n const shouldOverflow = maxVisibleActions !== undefined && allActions.length > maxVisibleActions\n const visibleActions = shouldOverflow\n ? allActions.slice(0, Math.max(maxVisibleActions - 1, 0))\n : allActions\n const overflowActions = shouldOverflow ? allActions.slice(visibleActions.length) : []\n\n const searchValue = externalSearchValue !== undefined ? externalSearchValue : internalSearchValue\n\n const handleSearchInput = (e: React.ChangeEvent<HTMLInputElement>) => {\n const val = e.target.value\n setInternalSearchValue(val)\n onSearchChange?.(val)\n }\n\n const handleClearSearch = () => {\n setInternalSearchValue('')\n onSearchChange?.('')\n }\n\n return (\n <header\n className={buildClassName(\n 'w-full transition-colors select-none z-30',\n sticky && 'sticky top-0',\n transparent\n ? 'bg-white/80 dark:bg-gray-900/80 backdrop-blur-md border-b border-[var(--ui-border-muted)]'\n : 'bg-white dark:bg-gray-900 border-b border-[var(--ui-border)] shadow-xs',\n 'pt-[env(safe-area-inset-top,0px)]',\n className,\n )}\n data-testid=\"mobile-header\"\n >\n {/* Main Header Row */}\n <div className=\"h-14 px-4 flex items-center justify-between gap-3 relative\">\n {/* Search Mode Header View */}\n {searchable && isSearchActive ? (\n <div\n className=\"flex-1 flex items-center gap-2\"\n data-testid=\"mobile-header-search-container\"\n >\n <div className=\"relative flex-1 flex items-center\">\n <span className=\"absolute left-3 pointer-events-none\">\n <SearchIcon />\n </span>\n <input\n type=\"text\"\n value={searchValue}\n onChange={handleSearchInput}\n placeholder={searchPlaceholder}\n autoFocus\n className=\"w-full pl-9 pr-8 py-1.5 text-xs bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-white rounded-full border border-transparent focus:border-blue-500 focus:outline-hidden transition-all\"\n data-testid=\"mobile-header-search-input\"\n />\n {searchValue && (\n <button\n type=\"button\"\n onClick={handleClearSearch}\n aria-label=\"Clear search\"\n className=\"absolute right-2.5 p-0.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 cursor-pointer\"\n >\n <ClearIcon />\n </button>\n )}\n </div>\n <button\n type=\"button\"\n onClick={() => setIsSearchActive(false)}\n className=\"text-xs font-semibold text-blue-600 dark:text-blue-400 px-2 py-1.5 rounded-lg hover:bg-blue-50 dark:hover:bg-blue-950/40 cursor-pointer\"\n >\n Cancel\n </button>\n </div>\n ) : (\n <>\n {/* Left Leading / Back Section. Omitted for a left-aligned title with nothing\n before it, so the title lines up with the page content; kept for a centred\n title, where the empty slot balances the actions. */}\n {hasLeadingSlot && (\n <div\n className={buildClassName(\n 'flex items-center gap-1 shrink-0 z-10',\n // A Back button is its own 40px target; reserve the width only for other content.\n !onBack && 'min-w-[40px]',\n )}\n data-testid=\"mobile-header-leading\"\n >\n {onBack ? (\n <button\n type=\"button\"\n onClick={onBack}\n aria-label={backLabel}\n // 40px touch target hanging into the row padding, so the chevron\n // itself sits on the content edge and the title follows closely.\n className=\"flex min-h-10 min-w-10 items-center justify-center gap-0.5 px-2.5 -ml-2.5 text-blue-600 dark:text-blue-400 hover:text-blue-700 rounded-xl transition-colors cursor-pointer\"\n data-testid=\"mobile-header-back-btn\"\n >\n <ChevronLeftIcon />\n <span className=\"text-xs font-semibold hidden sm:inline\">{backLabel}</span>\n </button>\n ) : (\n leading\n )}\n </div>\n )}\n\n {/* Title & Subtitle Section */}\n <div\n className={buildClassName(\n 'flex-1 min-w-0',\n // The row's gap already separates a left title from a leading element;\n // a centred title keeps even padding.\n titleAlign === 'center' ? 'px-2' : 'pr-2',\n titleAlign === 'center'\n ? 'absolute inset-x-12 text-center pointer-events-none flex flex-col items-center justify-center'\n : 'text-left',\n )}\n >\n {title && (\n <div\n className=\"text-sm font-bold text-gray-900 dark:text-white truncate pointer-events-auto\"\n data-testid=\"mobile-header-title\"\n >\n {title}\n </div>\n )}\n {subtitle && (\n <div\n className=\"text-[11px] text-gray-500 dark:text-gray-400 truncate pointer-events-auto\"\n data-testid=\"mobile-header-subtitle\"\n >\n {subtitle}\n </div>\n )}\n </div>\n\n {/* Right Actions Section */}\n <div className=\"flex items-center gap-1 min-w-[40px] shrink-0 justify-end z-10\">\n {searchable && (\n <button\n type=\"button\"\n onClick={() => setIsSearchActive(true)}\n aria-label=\"Search\"\n className=\"p-2 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-xl transition-colors cursor-pointer\"\n data-testid=\"mobile-header-search-toggle\"\n >\n <SearchIcon />\n </button>\n )}\n\n {visibleActions.map((action: MobileHeaderAction) => {\n const badgeColor = {\n danger: 'bg-red-500 text-white',\n info: 'bg-blue-500 text-white',\n success: 'bg-emerald-500 text-white',\n }[action.badgeVariant || 'danger']\n\n return (\n <button\n key={action.id}\n type=\"button\"\n disabled={action.disabled}\n onClick={action.onClick}\n aria-label={action.label}\n className={buildClassName(\n 'p-2 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-xl transition-colors relative cursor-pointer',\n action.disabled && 'opacity-40 cursor-not-allowed pointer-events-none',\n )}\n data-testid={`mobile-header-action-${action.id}`}\n >\n <span className=\"text-base\">{action.icon}</span>\n {action.badge !== undefined && action.badge !== null && (\n <span\n className={buildClassName(\n 'absolute top-1 right-1 min-w-[14px] h-3.5 px-0.5 rounded-full text-[9px] font-bold flex items-center justify-center shadow-xs',\n badgeColor,\n )}\n data-testid={`mobile-header-badge-${action.id}`}\n >\n {action.badge}\n </span>\n )}\n </button>\n )\n })}\n\n {overflowActions.length > 0 && (\n <button\n type=\"button\"\n onClick={() => setIsOverflowOpen(true)}\n aria-label={overflowLabel}\n aria-haspopup=\"dialog\"\n aria-expanded={isOverflowOpen}\n className=\"p-2 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-xl transition-colors cursor-pointer\"\n data-testid=\"mobile-header-overflow\"\n >\n <MoreIcon />\n </button>\n )}\n\n {trailing}\n </div>\n </>\n )}\n </div>\n\n {overflowActions.length > 0 && (\n <ActionSheet\n isOpen={isOverflowOpen}\n onClose={() => setIsOverflowOpen(false)}\n actions={overflowActions.map((action) => ({\n id: action.id,\n label: action.label,\n icon: action.icon,\n disabled: action.disabled,\n onClick: action.onClick,\n }))}\n />\n )}\n\n {/* Optional Bottom Sub-Header Slot (e.g. Category tabs or filter bar) */}\n {bottomSlot && (\n <div\n className=\"px-3 py-2 border-t border-[var(--ui-border-muted)]\"\n data-testid=\"mobile-header-bottom-slot\"\n >\n {bottomSlot}\n </div>\n )}\n </header>\n )\n}\n\nMobileHeader.displayName = 'MobileHeader'\n"],"names":["_jsx","_jsxs","_Fragment"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MobileHeader.js","sources":["../../../../../src/components/MobileHeader/MobileHeader.tsx"],"sourcesContent":["import React, { useState } from 'react'\nimport type { MobileHeaderProps, MobileHeaderAction } from './MobileHeader.types'\nimport { buildClassName } from '../../utils/build-classname'\nimport { ActionSheet } from '../ActionSheet'\n\nfunction ChevronLeftIcon() {\n return (\n <svg className=\"size-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2.5} d=\"M15 19l-7-7 7-7\" />\n </svg>\n )\n}\n\nfunction SearchIcon() {\n return (\n <svg className=\"size-4 text-gray-400\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"\n />\n </svg>\n )\n}\n\nfunction MoreIcon() {\n return (\n <svg className=\"size-5\" fill=\"currentColor\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"5\" r=\"1.75\" />\n <circle cx=\"12\" cy=\"12\" r=\"1.75\" />\n <circle cx=\"12\" cy=\"19\" r=\"1.75\" />\n </svg>\n )\n}\n\nfunction ClearIcon() {\n return (\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n )\n}\n\nexport function MobileHeader({\n title,\n subtitle,\n titleAlign = 'center',\n onBack,\n backLabel = 'Back',\n leading,\n actions,\n maxVisibleActions,\n overflowLabel = 'More actions',\n trailing,\n searchable = false,\n searchValue: externalSearchValue,\n onSearchChange,\n searchPlaceholder = 'Search...',\n sticky = true,\n transparent = false,\n bottomSlot,\n className = '',\n}: MobileHeaderProps) {\n const [isSearchActive, setIsSearchActive] = useState(false)\n const [internalSearchValue, setInternalSearchValue] = useState('')\n const [isOverflowOpen, setIsOverflowOpen] = useState(false)\n const hasLeadingSlot = Boolean(onBack || leading) || titleAlign === 'center'\n\n // The overflow button takes one of the visible slots.\n const allActions = actions ?? []\n const shouldOverflow = maxVisibleActions !== undefined && allActions.length > maxVisibleActions\n const visibleActions = shouldOverflow\n ? allActions.slice(0, Math.max(maxVisibleActions - 1, 0))\n : allActions\n const overflowActions = shouldOverflow ? allActions.slice(visibleActions.length) : []\n\n const searchValue = externalSearchValue !== undefined ? externalSearchValue : internalSearchValue\n\n const handleSearchInput = (e: React.ChangeEvent<HTMLInputElement>) => {\n const val = e.target.value\n setInternalSearchValue(val)\n onSearchChange?.(val)\n }\n\n const handleClearSearch = () => {\n setInternalSearchValue('')\n onSearchChange?.('')\n }\n\n return (\n <header\n className={buildClassName(\n 'w-full transition-colors select-none z-30',\n sticky && 'sticky top-0',\n transparent\n ? 'bg-white/80 dark:bg-gray-900/80 backdrop-blur-md border-b border-[var(--ui-border-muted)]'\n : 'bg-white dark:bg-gray-900 border-b border-[var(--ui-border)] shadow-xs',\n 'pt-[env(safe-area-inset-top,0px)]',\n className,\n )}\n data-testid=\"mobile-header\"\n >\n {/* Main Header Row */}\n <div className=\"h-14 px-4 flex items-center justify-between gap-2 relative\">\n {/* Search Mode Header View */}\n {searchable && isSearchActive ? (\n <div\n className=\"flex-1 flex items-center gap-2\"\n data-testid=\"mobile-header-search-container\"\n >\n <div className=\"relative flex-1 flex items-center\">\n <span className=\"absolute left-3 pointer-events-none\">\n <SearchIcon />\n </span>\n <input\n type=\"text\"\n value={searchValue}\n onChange={handleSearchInput}\n placeholder={searchPlaceholder}\n autoFocus\n className=\"w-full pl-9 pr-8 py-1.5 text-xs bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-white rounded-full border border-transparent focus:border-blue-500 focus:outline-hidden transition-all\"\n data-testid=\"mobile-header-search-input\"\n />\n {searchValue && (\n <button\n type=\"button\"\n onClick={handleClearSearch}\n aria-label=\"Clear search\"\n className=\"absolute right-2.5 p-0.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 cursor-pointer\"\n >\n <ClearIcon />\n </button>\n )}\n </div>\n <button\n type=\"button\"\n onClick={() => setIsSearchActive(false)}\n className=\"text-xs font-semibold text-blue-600 dark:text-blue-400 px-2 py-1.5 rounded-lg hover:bg-blue-50 dark:hover:bg-blue-950/40 cursor-pointer\"\n >\n Cancel\n </button>\n </div>\n ) : (\n <>\n {/* Left Leading / Back Section. Omitted for a left-aligned title with nothing\n before it, so the title lines up with the page content; kept for a centred\n title, where the empty slot balances the actions. */}\n {hasLeadingSlot && (\n <div\n className={buildClassName(\n 'flex items-center gap-1 shrink-0 z-10',\n // A Back button is its own 40px target; reserve the width only for other content.\n !onBack && 'min-w-[40px]',\n )}\n data-testid=\"mobile-header-leading\"\n >\n {onBack ? (\n <button\n type=\"button\"\n onClick={onBack}\n aria-label={backLabel}\n // 40px-tall touch target hanging into the row padding: the chevron\n // sits on the content edge, with no right padding, so the title\n // follows ~10px after it.\n className=\"flex min-h-10 min-w-8 items-center justify-start gap-0.5 pl-2.5 pr-0 sm:pr-2 -ml-2.5 text-blue-600 dark:text-blue-400 hover:text-blue-700 rounded-xl transition-colors cursor-pointer\"\n data-testid=\"mobile-header-back-btn\"\n >\n <ChevronLeftIcon />\n <span className=\"text-xs font-semibold hidden sm:inline\">{backLabel}</span>\n </button>\n ) : (\n leading\n )}\n </div>\n )}\n\n {/* Title & Subtitle Section */}\n <div\n className={buildClassName(\n 'flex-1 min-w-0',\n // The row's gap already separates a left title from a leading element;\n // a centred title keeps even padding.\n titleAlign === 'center' ? 'px-2' : 'pr-2',\n titleAlign === 'center'\n ? 'absolute inset-x-12 text-center pointer-events-none flex flex-col items-center justify-center'\n : 'text-left',\n )}\n >\n {title && (\n <div\n className=\"text-sm font-bold text-gray-900 dark:text-white truncate pointer-events-auto\"\n data-testid=\"mobile-header-title\"\n >\n {title}\n </div>\n )}\n {subtitle && (\n <div\n className=\"text-[11px] text-gray-500 dark:text-gray-400 truncate pointer-events-auto\"\n data-testid=\"mobile-header-subtitle\"\n >\n {subtitle}\n </div>\n )}\n </div>\n\n {/* Right Actions Section */}\n <div className=\"flex items-center gap-1 min-w-[40px] shrink-0 justify-end z-10\">\n {searchable && (\n <button\n type=\"button\"\n onClick={() => setIsSearchActive(true)}\n aria-label=\"Search\"\n className=\"p-2 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-xl transition-colors cursor-pointer\"\n data-testid=\"mobile-header-search-toggle\"\n >\n <SearchIcon />\n </button>\n )}\n\n {visibleActions.map((action: MobileHeaderAction) => {\n const badgeColor = {\n danger: 'bg-red-500 text-white',\n info: 'bg-blue-500 text-white',\n success: 'bg-emerald-500 text-white',\n }[action.badgeVariant || 'danger']\n\n return (\n <button\n key={action.id}\n type=\"button\"\n disabled={action.disabled}\n onClick={action.onClick}\n aria-label={action.label}\n className={buildClassName(\n 'p-2 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-xl transition-colors relative cursor-pointer',\n action.disabled && 'opacity-40 cursor-not-allowed pointer-events-none',\n )}\n data-testid={`mobile-header-action-${action.id}`}\n >\n <span className=\"text-base\">{action.icon}</span>\n {action.badge !== undefined && action.badge !== null && (\n <span\n className={buildClassName(\n 'absolute top-1 right-1 min-w-[14px] h-3.5 px-0.5 rounded-full text-[9px] font-bold flex items-center justify-center shadow-xs',\n badgeColor,\n )}\n data-testid={`mobile-header-badge-${action.id}`}\n >\n {action.badge}\n </span>\n )}\n </button>\n )\n })}\n\n {overflowActions.length > 0 && (\n <button\n type=\"button\"\n onClick={() => setIsOverflowOpen(true)}\n aria-label={overflowLabel}\n aria-haspopup=\"dialog\"\n aria-expanded={isOverflowOpen}\n className=\"p-2 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-xl transition-colors cursor-pointer\"\n data-testid=\"mobile-header-overflow\"\n >\n <MoreIcon />\n </button>\n )}\n\n {trailing}\n </div>\n </>\n )}\n </div>\n\n {overflowActions.length > 0 && (\n <ActionSheet\n isOpen={isOverflowOpen}\n onClose={() => setIsOverflowOpen(false)}\n actions={overflowActions.map((action) => ({\n id: action.id,\n label: action.label,\n icon: action.icon,\n disabled: action.disabled,\n onClick: action.onClick,\n }))}\n />\n )}\n\n {/* Optional Bottom Sub-Header Slot (e.g. Category tabs or filter bar) */}\n {bottomSlot && (\n <div\n className=\"px-3 py-2 border-t border-[var(--ui-border-muted)]\"\n data-testid=\"mobile-header-bottom-slot\"\n >\n {bottomSlot}\n </div>\n )}\n </header>\n )\n}\n\nMobileHeader.displayName = 'MobileHeader'\n"],"names":["_jsx","_jsxs","_Fragment"],"mappings":"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"}
|
|
@@ -84,7 +84,9 @@ const Toast = ({ id, title, message, type, variant = 'accent', action, icon, sho
|
|
|
84
84
|
const enterFrom = isTop ? 'opacity-0 -translate-y-3 scale-95' : 'opacity-0 translate-y-3 scale-95';
|
|
85
85
|
const leaveTo = isTop ? 'opacity-0 -translate-y-2 scale-95' : 'opacity-0 translate-y-2 scale-95';
|
|
86
86
|
const renderedIcon = icon !== null && icon !== void 0 ? icon : (variant === 'filled' ? filledIconMap[type] : iconMap[type]);
|
|
87
|
-
return (jsx(Transition, { appear: true, show: true, enter: "transform transition duration-300 ease-out", enterFrom: enterFrom, enterTo: "opacity-100 translate-y-0 scale-100", leave: "transition duration-200 ease-in", leaveFrom: "opacity-100 translate-y-0 scale-100", leaveTo: leaveTo, children: jsxs("div", { role: "status", id: id.toString(), className: buildClassName(
|
|
87
|
+
return (jsx(Transition, { appear: true, show: true, enter: "transform transition duration-300 ease-out", enterFrom: enterFrom, enterTo: "opacity-100 translate-y-0 scale-100", leave: "transition duration-200 ease-in", leaveFrom: "opacity-100 translate-y-0 scale-100", leaveTo: leaveTo, children: jsxs("div", { role: "status", id: id.toString(), className: buildClassName(
|
|
88
|
+
// Never wider than the viewport minus a 16px gutter each side.
|
|
89
|
+
'relative overflow-hidden flex items-start gap-3 rounded-xl px-4 py-3.5 min-w-[min(280px,calc(100vw-2rem))] max-w-[min(28rem,calc(100vw-2rem))] transition-all', variantStyleMap[variant][type], className), children: [jsx("div", { className: "shrink-0 mt-0.5", children: variant === 'filled' ? (renderedIcon) : (jsx(TextContent, { className: buildClassName(textColorMap[type]), children: renderedIcon })) }), jsxs("div", { className: "flex-1 flex flex-col gap-0.5", children: [title && (jsx("div", { className: buildClassName('font-semibold text-sm leading-snug', variant === 'filled' ? 'text-white' : 'text-gray-900 dark:text-gray-100'), children: title })), jsx("div", { className: buildClassName('text-sm font-medium leading-relaxed', variant === 'filled' ? 'text-white/95' : 'text-gray-700 dark:text-gray-300'), children: message }), action && (jsx("div", { className: "mt-1.5", children: jsx("button", { type: "button", onClick: action.onClick, className: buildClassName('text-xs font-semibold underline underline-offset-2 transition-opacity focus:outline-none', variant === 'filled'
|
|
88
90
|
? 'text-white hover:opacity-85'
|
|
89
91
|
: 'text-[var(--ui-primary)] hover:opacity-80'), children: action.label }) }))] }), jsx(Button, { onClick: onClose, "aria-label": "Close toast", className: buildClassName('shrink-0 mt-0.5 opacity-60 hover:opacity-100 focus-visible:opacity-100 transition', variant === 'filled' && 'text-white hover:text-white focus-visible:outline-current'), iconOnly: true, size: "xs", variant: "plain", theme: variant === 'filled' ? 'primary' : 'secondary', children: jsx(SvgX, { className: "size-4" }) }), showProgressBar && duration > 0 && (jsx("div", { className: "absolute bottom-0 left-0 right-0 h-1 bg-black/10 dark:bg-white/10 overflow-hidden", children: jsx("div", { className: buildClassName('h-full transition-all duration-75 ease-linear', variant === 'filled' ? 'bg-white/70' : progressBarColorMap[type]), style: { width: `${Math.max(0, progress)}%` } }) }))] }) }));
|
|
90
92
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Toast.js","sources":["../../../../../src/components/Toast/Toast.tsx"],"sourcesContent":["import React, { useEffect, useState } from 'react'\nimport { Transition } from '@headlessui/react'\nimport XIcon from '../Icons/X.svg'\nimport CheckCircle from '../Icons/CircleCheck.svg'\nimport AlertCircle from '../Icons/CircleAlert.svg'\nimport Info from '../Icons/Info.svg'\nimport AlertTriangle from '../Icons/TriangleAlert.svg'\n\nimport { Button } from '../Button'\nimport { TextContent } from '../TextContent'\nimport { buildClassName } from '../../utils/build-classname'\n\nimport type { ToastProps, ToastType, ToastVariant } from './Toast.types'\n\n/**\n * Icons for each toast type\n */\nconst iconMap: Record<ToastType, React.ReactNode> = {\n success: <CheckCircle className=\"size-5 text-[var(--ui-success)]\" />,\n error: <AlertCircle className=\"size-5 text-[var(--ui-danger)]\" />,\n info: <Info className=\"size-5 text-[var(--ui-info)]\" />,\n warning: <AlertTriangle className=\"size-5 text-[var(--ui-warning)]\" />,\n}\n\nconst filledIconMap: Record<ToastType, React.ReactNode> = {\n success: <CheckCircle className=\"size-5 text-white\" />,\n error: <AlertCircle className=\"size-5 text-white\" />,\n info: <Info className=\"size-5 text-white\" />,\n warning: <AlertTriangle className=\"size-5 text-white\" />,\n}\n\nconst textColorMap: Record<ToastType, string> = {\n success: 'text-[var(--ui-success-text)]',\n error: 'text-[var(--ui-danger-text)]',\n info: 'text-[var(--ui-info-text)]',\n warning: 'text-[var(--ui-warning-text)]',\n}\n\nconst progressBarColorMap: Record<ToastType, string> = {\n success: 'bg-[var(--ui-success)]',\n error: 'bg-[var(--ui-danger)]',\n info: 'bg-[var(--ui-info)]',\n warning: 'bg-[var(--ui-warning)]',\n}\n\nconst variantStyleMap: Record<ToastVariant, Record<ToastType, string>> = {\n accent: {\n success:\n 'bg-white dark:bg-gray-900 border-l-4 border-[var(--ui-success)] text-gray-900 dark:text-gray-100 shadow-lg border-y border-r border-[var(--ui-border)]',\n error:\n 'bg-white dark:bg-gray-900 border-l-4 border-[var(--ui-danger)] text-gray-900 dark:text-gray-100 shadow-lg border-y border-r border-[var(--ui-border)]',\n info: 'bg-white dark:bg-gray-900 border-l-4 border-[var(--ui-info)] text-gray-900 dark:text-gray-100 shadow-lg border-y border-r border-[var(--ui-border)]',\n warning:\n 'bg-white dark:bg-gray-900 border-l-4 border-[var(--ui-warning)] text-gray-900 dark:text-gray-100 shadow-lg border-y border-r border-[var(--ui-border)]',\n },\n filled: {\n success: 'bg-[var(--ui-success)] text-white shadow-lg',\n error: 'bg-[var(--ui-danger)] text-white shadow-lg',\n info: 'bg-[var(--ui-info)] text-white shadow-lg',\n warning: 'bg-[var(--ui-warning)] text-white shadow-lg',\n },\n outlined: {\n success:\n 'bg-white dark:bg-gray-900 border-2 border-[var(--ui-success)] text-gray-900 dark:text-gray-100 shadow-md',\n error:\n 'bg-white dark:bg-gray-900 border-2 border-[var(--ui-danger)] text-gray-900 dark:text-gray-100 shadow-md',\n info: 'bg-white dark:bg-gray-900 border-2 border-[var(--ui-info)] text-gray-900 dark:text-gray-100 shadow-md',\n warning:\n 'bg-white dark:bg-gray-900 border-2 border-[var(--ui-warning)] text-gray-900 dark:text-gray-100 shadow-md',\n },\n glass: {\n success:\n 'backdrop-blur-md bg-white/85 dark:bg-gray-900/85 border border-emerald-500/30 text-gray-900 dark:text-gray-100 shadow-xl',\n error:\n 'backdrop-blur-md bg-white/85 dark:bg-gray-900/85 border border-rose-500/30 text-gray-900 dark:text-gray-100 shadow-xl',\n info: 'backdrop-blur-md bg-white/85 dark:bg-gray-900/85 border border-blue-500/30 text-gray-900 dark:text-gray-100 shadow-xl',\n warning:\n 'backdrop-blur-md bg-white/85 dark:bg-gray-900/85 border border-amber-500/30 text-gray-900 dark:text-gray-100 shadow-xl',\n },\n}\n\nexport const Toast: React.FC<ToastProps & { isTop?: boolean }> = ({\n id,\n title,\n message,\n type,\n variant = 'accent',\n action,\n icon,\n showProgressBar = true,\n duration = 4000,\n isPaused = false,\n onClose,\n className,\n isTop = true,\n}) => {\n const [progress, setProgress] = useState(100)\n\n useEffect(() => {\n if (!showProgressBar || !duration || isPaused) return\n\n const intervalTime = 50\n const step = (intervalTime / duration) * 100\n\n const timer = setInterval(() => {\n setProgress((prev) => {\n if (prev <= 0) {\n clearInterval(timer)\n return 0\n }\n return prev - step\n })\n }, intervalTime)\n\n return () => clearInterval(timer)\n }, [showProgressBar, duration, isPaused])\n\n const enterFrom = isTop ? 'opacity-0 -translate-y-3 scale-95' : 'opacity-0 translate-y-3 scale-95'\n const leaveTo = isTop ? 'opacity-0 -translate-y-2 scale-95' : 'opacity-0 translate-y-2 scale-95'\n\n const renderedIcon = icon ?? (variant === 'filled' ? filledIconMap[type] : iconMap[type])\n\n return (\n <Transition\n appear\n show\n enter=\"transform transition duration-300 ease-out\"\n enterFrom={enterFrom}\n enterTo=\"opacity-100 translate-y-0 scale-100\"\n leave=\"transition duration-200 ease-in\"\n leaveFrom=\"opacity-100 translate-y-0 scale-100\"\n leaveTo={leaveTo}\n >\n <div\n role=\"status\"\n id={id.toString()}\n className={buildClassName(\n 'relative overflow-hidden flex items-start gap-3 rounded-xl px-4 py-3.5 min-w-[280px] max-w-md transition-all',\n variantStyleMap[variant][type],\n className,\n )}\n >\n {/* Icon */}\n <div className=\"shrink-0 mt-0.5\">\n {variant === 'filled' ? (\n renderedIcon\n ) : (\n <TextContent className={buildClassName(textColorMap[type])}>{renderedIcon}</TextContent>\n )}\n </div>\n\n {/* Content Body */}\n <div className=\"flex-1 flex flex-col gap-0.5\">\n {title && (\n <div\n className={buildClassName(\n 'font-semibold text-sm leading-snug',\n variant === 'filled' ? 'text-white' : 'text-gray-900 dark:text-gray-100',\n )}\n >\n {title}\n </div>\n )}\n <div\n className={buildClassName(\n 'text-sm font-medium leading-relaxed',\n variant === 'filled' ? 'text-white/95' : 'text-gray-700 dark:text-gray-300',\n )}\n >\n {message}\n </div>\n\n {/* Action Button */}\n {action && (\n <div className=\"mt-1.5\">\n <button\n type=\"button\"\n onClick={action.onClick}\n className={buildClassName(\n 'text-xs font-semibold underline underline-offset-2 transition-opacity focus:outline-none',\n variant === 'filled'\n ? 'text-white hover:opacity-85'\n : 'text-[var(--ui-primary)] hover:opacity-80',\n )}\n >\n {action.label}\n </button>\n </div>\n )}\n </div>\n\n {/* Close button */}\n <Button\n onClick={onClose}\n aria-label=\"Close toast\"\n className={buildClassName(\n 'shrink-0 mt-0.5 opacity-60 hover:opacity-100 focus-visible:opacity-100 transition',\n variant === 'filled' && 'text-white hover:text-white focus-visible:outline-current',\n )}\n iconOnly\n size=\"xs\"\n variant=\"plain\"\n theme={variant === 'filled' ? 'primary' : 'secondary'}\n >\n <XIcon className=\"size-4\" />\n </Button>\n\n {/* Progress Bar Countdown */}\n {showProgressBar && duration > 0 && (\n <div className=\"absolute bottom-0 left-0 right-0 h-1 bg-black/10 dark:bg-white/10 overflow-hidden\">\n <div\n className={buildClassName(\n 'h-full transition-all duration-75 ease-linear',\n variant === 'filled' ? 'bg-white/70' : progressBarColorMap[type],\n )}\n style={{ width: `${Math.max(0, progress)}%` }}\n />\n </div>\n )}\n </div>\n </Transition>\n )\n}\n\nToast.displayName = 'Toast'\n"],"names":["_jsx","CheckCircle","AlertCircle","Info","AlertTriangle","_jsxs","XIcon"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Toast.js","sources":["../../../../../src/components/Toast/Toast.tsx"],"sourcesContent":["import React, { useEffect, useState } from 'react'\nimport { Transition } from '@headlessui/react'\nimport XIcon from '../Icons/X.svg'\nimport CheckCircle from '../Icons/CircleCheck.svg'\nimport AlertCircle from '../Icons/CircleAlert.svg'\nimport Info from '../Icons/Info.svg'\nimport AlertTriangle from '../Icons/TriangleAlert.svg'\n\nimport { Button } from '../Button'\nimport { TextContent } from '../TextContent'\nimport { buildClassName } from '../../utils/build-classname'\n\nimport type { ToastProps, ToastType, ToastVariant } from './Toast.types'\n\n/**\n * Icons for each toast type\n */\nconst iconMap: Record<ToastType, React.ReactNode> = {\n success: <CheckCircle className=\"size-5 text-[var(--ui-success)]\" />,\n error: <AlertCircle className=\"size-5 text-[var(--ui-danger)]\" />,\n info: <Info className=\"size-5 text-[var(--ui-info)]\" />,\n warning: <AlertTriangle className=\"size-5 text-[var(--ui-warning)]\" />,\n}\n\nconst filledIconMap: Record<ToastType, React.ReactNode> = {\n success: <CheckCircle className=\"size-5 text-white\" />,\n error: <AlertCircle className=\"size-5 text-white\" />,\n info: <Info className=\"size-5 text-white\" />,\n warning: <AlertTriangle className=\"size-5 text-white\" />,\n}\n\nconst textColorMap: Record<ToastType, string> = {\n success: 'text-[var(--ui-success-text)]',\n error: 'text-[var(--ui-danger-text)]',\n info: 'text-[var(--ui-info-text)]',\n warning: 'text-[var(--ui-warning-text)]',\n}\n\nconst progressBarColorMap: Record<ToastType, string> = {\n success: 'bg-[var(--ui-success)]',\n error: 'bg-[var(--ui-danger)]',\n info: 'bg-[var(--ui-info)]',\n warning: 'bg-[var(--ui-warning)]',\n}\n\nconst variantStyleMap: Record<ToastVariant, Record<ToastType, string>> = {\n accent: {\n success:\n 'bg-white dark:bg-gray-900 border-l-4 border-[var(--ui-success)] text-gray-900 dark:text-gray-100 shadow-lg border-y border-r border-[var(--ui-border)]',\n error:\n 'bg-white dark:bg-gray-900 border-l-4 border-[var(--ui-danger)] text-gray-900 dark:text-gray-100 shadow-lg border-y border-r border-[var(--ui-border)]',\n info: 'bg-white dark:bg-gray-900 border-l-4 border-[var(--ui-info)] text-gray-900 dark:text-gray-100 shadow-lg border-y border-r border-[var(--ui-border)]',\n warning:\n 'bg-white dark:bg-gray-900 border-l-4 border-[var(--ui-warning)] text-gray-900 dark:text-gray-100 shadow-lg border-y border-r border-[var(--ui-border)]',\n },\n filled: {\n success: 'bg-[var(--ui-success)] text-white shadow-lg',\n error: 'bg-[var(--ui-danger)] text-white shadow-lg',\n info: 'bg-[var(--ui-info)] text-white shadow-lg',\n warning: 'bg-[var(--ui-warning)] text-white shadow-lg',\n },\n outlined: {\n success:\n 'bg-white dark:bg-gray-900 border-2 border-[var(--ui-success)] text-gray-900 dark:text-gray-100 shadow-md',\n error:\n 'bg-white dark:bg-gray-900 border-2 border-[var(--ui-danger)] text-gray-900 dark:text-gray-100 shadow-md',\n info: 'bg-white dark:bg-gray-900 border-2 border-[var(--ui-info)] text-gray-900 dark:text-gray-100 shadow-md',\n warning:\n 'bg-white dark:bg-gray-900 border-2 border-[var(--ui-warning)] text-gray-900 dark:text-gray-100 shadow-md',\n },\n glass: {\n success:\n 'backdrop-blur-md bg-white/85 dark:bg-gray-900/85 border border-emerald-500/30 text-gray-900 dark:text-gray-100 shadow-xl',\n error:\n 'backdrop-blur-md bg-white/85 dark:bg-gray-900/85 border border-rose-500/30 text-gray-900 dark:text-gray-100 shadow-xl',\n info: 'backdrop-blur-md bg-white/85 dark:bg-gray-900/85 border border-blue-500/30 text-gray-900 dark:text-gray-100 shadow-xl',\n warning:\n 'backdrop-blur-md bg-white/85 dark:bg-gray-900/85 border border-amber-500/30 text-gray-900 dark:text-gray-100 shadow-xl',\n },\n}\n\nexport const Toast: React.FC<ToastProps & { isTop?: boolean }> = ({\n id,\n title,\n message,\n type,\n variant = 'accent',\n action,\n icon,\n showProgressBar = true,\n duration = 4000,\n isPaused = false,\n onClose,\n className,\n isTop = true,\n}) => {\n const [progress, setProgress] = useState(100)\n\n useEffect(() => {\n if (!showProgressBar || !duration || isPaused) return\n\n const intervalTime = 50\n const step = (intervalTime / duration) * 100\n\n const timer = setInterval(() => {\n setProgress((prev) => {\n if (prev <= 0) {\n clearInterval(timer)\n return 0\n }\n return prev - step\n })\n }, intervalTime)\n\n return () => clearInterval(timer)\n }, [showProgressBar, duration, isPaused])\n\n const enterFrom = isTop ? 'opacity-0 -translate-y-3 scale-95' : 'opacity-0 translate-y-3 scale-95'\n const leaveTo = isTop ? 'opacity-0 -translate-y-2 scale-95' : 'opacity-0 translate-y-2 scale-95'\n\n const renderedIcon = icon ?? (variant === 'filled' ? filledIconMap[type] : iconMap[type])\n\n return (\n <Transition\n appear\n show\n enter=\"transform transition duration-300 ease-out\"\n enterFrom={enterFrom}\n enterTo=\"opacity-100 translate-y-0 scale-100\"\n leave=\"transition duration-200 ease-in\"\n leaveFrom=\"opacity-100 translate-y-0 scale-100\"\n leaveTo={leaveTo}\n >\n <div\n role=\"status\"\n id={id.toString()}\n className={buildClassName(\n // Never wider than the viewport minus a 16px gutter each side.\n 'relative overflow-hidden flex items-start gap-3 rounded-xl px-4 py-3.5 min-w-[min(280px,calc(100vw-2rem))] max-w-[min(28rem,calc(100vw-2rem))] transition-all',\n variantStyleMap[variant][type],\n className,\n )}\n >\n {/* Icon */}\n <div className=\"shrink-0 mt-0.5\">\n {variant === 'filled' ? (\n renderedIcon\n ) : (\n <TextContent className={buildClassName(textColorMap[type])}>{renderedIcon}</TextContent>\n )}\n </div>\n\n {/* Content Body */}\n <div className=\"flex-1 flex flex-col gap-0.5\">\n {title && (\n <div\n className={buildClassName(\n 'font-semibold text-sm leading-snug',\n variant === 'filled' ? 'text-white' : 'text-gray-900 dark:text-gray-100',\n )}\n >\n {title}\n </div>\n )}\n <div\n className={buildClassName(\n 'text-sm font-medium leading-relaxed',\n variant === 'filled' ? 'text-white/95' : 'text-gray-700 dark:text-gray-300',\n )}\n >\n {message}\n </div>\n\n {/* Action Button */}\n {action && (\n <div className=\"mt-1.5\">\n <button\n type=\"button\"\n onClick={action.onClick}\n className={buildClassName(\n 'text-xs font-semibold underline underline-offset-2 transition-opacity focus:outline-none',\n variant === 'filled'\n ? 'text-white hover:opacity-85'\n : 'text-[var(--ui-primary)] hover:opacity-80',\n )}\n >\n {action.label}\n </button>\n </div>\n )}\n </div>\n\n {/* Close button */}\n <Button\n onClick={onClose}\n aria-label=\"Close toast\"\n className={buildClassName(\n 'shrink-0 mt-0.5 opacity-60 hover:opacity-100 focus-visible:opacity-100 transition',\n variant === 'filled' && 'text-white hover:text-white focus-visible:outline-current',\n )}\n iconOnly\n size=\"xs\"\n variant=\"plain\"\n theme={variant === 'filled' ? 'primary' : 'secondary'}\n >\n <XIcon className=\"size-4\" />\n </Button>\n\n {/* Progress Bar Countdown */}\n {showProgressBar && duration > 0 && (\n <div className=\"absolute bottom-0 left-0 right-0 h-1 bg-black/10 dark:bg-white/10 overflow-hidden\">\n <div\n className={buildClassName(\n 'h-full transition-all duration-75 ease-linear',\n variant === 'filled' ? 'bg-white/70' : progressBarColorMap[type],\n )}\n style={{ width: `${Math.max(0, progress)}%` }}\n />\n </div>\n )}\n </div>\n </Transition>\n )\n}\n\nToast.displayName = 'Toast'\n"],"names":["_jsx","CheckCircle","AlertCircle","Info","AlertTriangle","_jsxs","XIcon"],"mappings":";;;;;;;;;;;;AAcA;;AAEG;AACH,MAAM,OAAO,GAAuC;AAClD,IAAA,OAAO,EAAEA,GAAA,CAACC,cAAW,IAAC,SAAS,EAAC,iCAAiC,EAAA,CAAG;AACpE,IAAA,KAAK,EAAED,GAAA,CAACE,cAAW,IAAC,SAAS,EAAC,gCAAgC,EAAA,CAAG;AACjE,IAAA,IAAI,EAAEF,GAAA,CAACG,OAAI,IAAC,SAAS,EAAC,8BAA8B,EAAA,CAAG;AACvD,IAAA,OAAO,EAAEH,GAAA,CAACI,gBAAa,IAAC,SAAS,EAAC,iCAAiC,EAAA,CAAG;CACvE;AAED,MAAM,aAAa,GAAuC;AACxD,IAAA,OAAO,EAAEJ,GAAA,CAACC,cAAW,IAAC,SAAS,EAAC,mBAAmB,EAAA,CAAG;AACtD,IAAA,KAAK,EAAED,GAAA,CAACE,cAAW,IAAC,SAAS,EAAC,mBAAmB,EAAA,CAAG;AACpD,IAAA,IAAI,EAAEF,GAAA,CAACG,OAAI,IAAC,SAAS,EAAC,mBAAmB,EAAA,CAAG;AAC5C,IAAA,OAAO,EAAEH,GAAA,CAACI,gBAAa,IAAC,SAAS,EAAC,mBAAmB,EAAA,CAAG;CACzD;AAED,MAAM,YAAY,GAA8B;AAC9C,IAAA,OAAO,EAAE,+BAA+B;AACxC,IAAA,KAAK,EAAE,8BAA8B;AACrC,IAAA,IAAI,EAAE,4BAA4B;AAClC,IAAA,OAAO,EAAE,+BAA+B;CACzC;AAED,MAAM,mBAAmB,GAA8B;AACrD,IAAA,OAAO,EAAE,wBAAwB;AACjC,IAAA,KAAK,EAAE,uBAAuB;AAC9B,IAAA,IAAI,EAAE,qBAAqB;AAC3B,IAAA,OAAO,EAAE,wBAAwB;CAClC;AAED,MAAM,eAAe,GAAoD;AACvE,IAAA,MAAM,EAAE;AACN,QAAA,OAAO,EACL,wJAAwJ;AAC1J,QAAA,KAAK,EACH,uJAAuJ;AACzJ,QAAA,IAAI,EAAE,qJAAqJ;AAC3J,QAAA,OAAO,EACL,wJAAwJ;AAC3J,KAAA;AACD,IAAA,MAAM,EAAE;AACN,QAAA,OAAO,EAAE,6CAA6C;AACtD,QAAA,KAAK,EAAE,4CAA4C;AACnD,QAAA,IAAI,EAAE,0CAA0C;AAChD,QAAA,OAAO,EAAE,6CAA6C;AACvD,KAAA;AACD,IAAA,QAAQ,EAAE;AACR,QAAA,OAAO,EACL,0GAA0G;AAC5G,QAAA,KAAK,EACH,yGAAyG;AAC3G,QAAA,IAAI,EAAE,uGAAuG;AAC7G,QAAA,OAAO,EACL,0GAA0G;AAC7G,KAAA;AACD,IAAA,KAAK,EAAE;AACL,QAAA,OAAO,EACL,0HAA0H;AAC5H,QAAA,KAAK,EACH,uHAAuH;AACzH,QAAA,IAAI,EAAE,uHAAuH;AAC7H,QAAA,OAAO,EACL,wHAAwH;AAC3H,KAAA;CACF;AAEM,MAAM,KAAK,GAA+C,CAAC,EAChE,EAAE,EACF,KAAK,EACL,OAAO,EACP,IAAI,EACJ,OAAO,GAAG,QAAQ,EAClB,MAAM,EACN,IAAI,EACJ,eAAe,GAAG,IAAI,EACtB,QAAQ,GAAG,IAAI,EACf,QAAQ,GAAG,KAAK,EAChB,OAAO,EACP,SAAS,EACT,KAAK,GAAG,IAAI,GACb,KAAI;IACH,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC;IAE7C,SAAS,CAAC,MAAK;AACb,QAAA,IAAI,CAAC,eAAe,IAAI,CAAC,QAAQ,IAAI,QAAQ;YAAE;QAE/C,MAAM,YAAY,GAAG,EAAE;QACvB,MAAM,IAAI,GAAG,CAAC,YAAY,GAAG,QAAQ,IAAI,GAAG;AAE5C,QAAA,MAAM,KAAK,GAAG,WAAW,CAAC,MAAK;AAC7B,YAAA,WAAW,CAAC,CAAC,IAAI,KAAI;AACnB,gBAAA,IAAI,IAAI,IAAI,CAAC,EAAE;oBACb,aAAa,CAAC,KAAK,CAAC;AACpB,oBAAA,OAAO,CAAC;gBACV;gBACA,OAAO,IAAI,GAAG,IAAI;AACpB,YAAA,CAAC,CAAC;QACJ,CAAC,EAAE,YAAY,CAAC;AAEhB,QAAA,OAAO,MAAM,aAAa,CAAC,KAAK,CAAC;IACnC,CAAC,EAAE,CAAC,eAAe,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAEzC,MAAM,SAAS,GAAG,KAAK,GAAG,mCAAmC,GAAG,kCAAkC;IAClG,MAAM,OAAO,GAAG,KAAK,GAAG,mCAAmC,GAAG,kCAAkC;IAEhG,MAAM,YAAY,GAAG,IAAI,KAAA,IAAA,IAAJ,IAAI,KAAA,MAAA,GAAJ,IAAI,IAAK,OAAO,KAAK,QAAQ,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzF,QACEJ,IAAC,UAAU,EAAA,EACT,MAAM,EAAA,IAAA,EACN,IAAI,QACJ,KAAK,EAAC,4CAA4C,EAClD,SAAS,EAAE,SAAS,EACpB,OAAO,EAAC,qCAAqC,EAC7C,KAAK,EAAC,iCAAiC,EACvC,SAAS,EAAC,qCAAqC,EAC/C,OAAO,EAAE,OAAO,EAAA,QAAA,EAEhBK,cACE,IAAI,EAAC,QAAQ,EACb,EAAE,EAAE,EAAE,CAAC,QAAQ,EAAE,EACjB,SAAS,EAAE,cAAc;;AAEvB,YAAA,+JAA+J,EAC/J,eAAe,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,EAC9B,SAAS,CACV,EAAA,QAAA,EAAA,CAGDL,GAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,iBAAiB,EAAA,QAAA,EAC7B,OAAO,KAAK,QAAQ,IACnB,YAAY,KAEZA,GAAA,CAAC,WAAW,EAAA,EAAC,SAAS,EAAE,cAAc,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,EAAA,QAAA,EAAG,YAAY,EAAA,CAAe,CACzF,EAAA,CACG,EAGNK,IAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,8BAA8B,EAAA,QAAA,EAAA,CAC1C,KAAK,KACJL,GAAA,CAAA,KAAA,EAAA,EACE,SAAS,EAAE,cAAc,CACvB,oCAAoC,EACpC,OAAO,KAAK,QAAQ,GAAG,YAAY,GAAG,kCAAkC,CACzE,EAAA,QAAA,EAEA,KAAK,EAAA,CACF,CACP,EACDA,GAAA,CAAA,KAAA,EAAA,EACE,SAAS,EAAE,cAAc,CACvB,qCAAqC,EACrC,OAAO,KAAK,QAAQ,GAAG,eAAe,GAAG,kCAAkC,CAC5E,EAAA,QAAA,EAEA,OAAO,EAAA,CACJ,EAGL,MAAM,KACLA,GAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,QAAQ,EAAA,QAAA,EACrBA,GAAA,CAAA,QAAA,EAAA,EACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,MAAM,CAAC,OAAO,EACvB,SAAS,EAAE,cAAc,CACvB,0FAA0F,EAC1F,OAAO,KAAK;AACV,sCAAE;AACF,sCAAE,2CAA2C,CAChD,EAAA,QAAA,EAEA,MAAM,CAAC,KAAK,EAAA,CACN,EAAA,CACL,CACP,CAAA,EAAA,CACG,EAGNA,GAAA,CAAC,MAAM,EAAA,EACL,OAAO,EAAE,OAAO,EAAA,YAAA,EACL,aAAa,EACxB,SAAS,EAAE,cAAc,CACvB,mFAAmF,EACnF,OAAO,KAAK,QAAQ,IAAI,2DAA2D,CACpF,EACD,QAAQ,EAAA,IAAA,EACR,IAAI,EAAC,IAAI,EACT,OAAO,EAAC,OAAO,EACf,KAAK,EAAE,OAAO,KAAK,QAAQ,GAAG,SAAS,GAAG,WAAW,EAAA,QAAA,EAErDA,GAAA,CAACM,IAAK,EAAA,EAAC,SAAS,EAAC,QAAQ,EAAA,CAAG,EAAA,CACrB,EAGR,eAAe,IAAI,QAAQ,GAAG,CAAC,KAC9BN,GAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,mFAAmF,EAAA,QAAA,EAChGA,GAAA,CAAA,KAAA,EAAA,EACE,SAAS,EAAE,cAAc,CACvB,+CAA+C,EAC/C,OAAO,KAAK,QAAQ,GAAG,aAAa,GAAG,mBAAmB,CAAC,IAAI,CAAC,CACjE,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,CAAA,EAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA,CAAA,CAAG,EAAE,EAAA,CAC7C,EAAA,CACE,CACP,CAAA,EAAA,CACG,EAAA,CACK;AAEjB;AAEA,KAAK,CAAC,WAAW,GAAG,OAAO;;;;"}
|