@pk-design/react-tailwind 1.10.0 → 1.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/MobileHeader/MobileHeader.js +2 -1
- package/dist/cjs/components/MobileHeader/MobileHeader.js.map +1 -1
- package/dist/es/components/MobileHeader/MobileHeader.js +2 -1
- package/dist/es/components/MobileHeader/MobileHeader.js.map +1 -1
- package/dist/react-tailwind.cjs.js +2 -1
- package/dist/react-tailwind.cjs.js.map +1 -1
- package/dist/react-tailwind.es.js +2 -1
- package/dist/react-tailwind.es.js.map +1 -1
- package/package.json +1 -1
|
@@ -21,6 +21,7 @@ function MobileHeader({ title, subtitle, titleAlign = 'center', onBack, backLabe
|
|
|
21
21
|
const [isSearchActive, setIsSearchActive] = React.useState(false);
|
|
22
22
|
const [internalSearchValue, setInternalSearchValue] = React.useState('');
|
|
23
23
|
const [isOverflowOpen, setIsOverflowOpen] = React.useState(false);
|
|
24
|
+
const hasLeadingSlot = Boolean(onBack || leading) || titleAlign === 'center';
|
|
24
25
|
// The overflow button takes one of the visible slots.
|
|
25
26
|
const allActions = actions !== null && actions !== void 0 ? actions : [];
|
|
26
27
|
const shouldOverflow = maxVisibleActions !== undefined && allActions.length > maxVisibleActions;
|
|
@@ -40,7 +41,7 @@ function MobileHeader({ title, subtitle, titleAlign = 'center', onBack, backLabe
|
|
|
40
41
|
};
|
|
41
42
|
return (jsxRuntime.jsxs("header", { className: buildClassname.buildClassName('w-full transition-colors select-none z-30', sticky && 'sticky top-0', transparent
|
|
42
43
|
? 'bg-white/80 dark:bg-gray-900/80 backdrop-blur-md border-b border-[var(--ui-border-muted)]'
|
|
43
|
-
: '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-
|
|
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-3 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: "flex items-center gap-1 min-w-[40px] shrink-0 z-10", "data-testid": "mobile-header-leading", children: onBack ? (jsxRuntime.jsxs("button", { type: "button", onClick: onBack, "aria-label": backLabel, className: "flex items-center gap-0.5 text-blue-600 dark:text-blue-400 hover:text-blue-700 p-1 -ml-1 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', hasLeadingSlot ? 'px-2' : 'pr-2', titleAlign === 'center'
|
|
44
45
|
? 'absolute inset-x-12 text-center pointer-events-none flex flex-col items-center justify-center'
|
|
45
46
|
: 'text-left'), children: [title && (jsxRuntime.jsx("div", { className: "text-sm font-bold text-gray-900 dark:text-white truncate pointer-events-auto", "data-testid": "mobile-header-title", children: title })), subtitle && (jsxRuntime.jsx("div", { className: "text-[11px] text-gray-500 dark:text-gray-400 truncate pointer-events-auto", "data-testid": "mobile-header-subtitle", children: subtitle }))] }), jsxRuntime.jsxs("div", { className: "flex items-center gap-1 min-w-[40px] shrink-0 justify-end z-10", children: [searchable && (jsxRuntime.jsx("button", { type: "button", onClick: () => setIsSearchActive(true), "aria-label": "Search", 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", "data-testid": "mobile-header-search-toggle", children: jsxRuntime.jsx(SearchIcon, {}) })), visibleActions.map((action) => {
|
|
46
47
|
const badgeColor = {
|
|
@@ -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\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-3.5 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 */}\n <div className=\"flex items-center gap-1 min-w-[40px] shrink-0 z-10\">\n {onBack ? (\n <button\n type=\"button\"\n onClick={onBack}\n aria-label={backLabel}\n className=\"flex items-center gap-0.5 text-blue-600 dark:text-blue-400 hover:text-blue-700 p-1 -ml-1 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 {/* Title & Subtitle Section */}\n <div\n className={buildClassName(\n 'flex-1 min-w-0 px-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-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=\"flex items-center gap-1 min-w-[40px] shrink-0 z-10\"\n data-testid=\"mobile-header-leading\"\n >\n {onBack ? (\n <button\n type=\"button\"\n onClick={onBack}\n aria-label={backLabel}\n className=\"flex items-center gap-0.5 text-blue-600 dark:text-blue-400 hover:text-blue-700 p-1 -ml-1 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 hasLeadingSlot ? '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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"}
|
|
@@ -19,6 +19,7 @@ function MobileHeader({ title, subtitle, titleAlign = 'center', onBack, backLabe
|
|
|
19
19
|
const [isSearchActive, setIsSearchActive] = useState(false);
|
|
20
20
|
const [internalSearchValue, setInternalSearchValue] = useState('');
|
|
21
21
|
const [isOverflowOpen, setIsOverflowOpen] = useState(false);
|
|
22
|
+
const hasLeadingSlot = Boolean(onBack || leading) || titleAlign === 'center';
|
|
22
23
|
// The overflow button takes one of the visible slots.
|
|
23
24
|
const allActions = actions !== null && actions !== void 0 ? actions : [];
|
|
24
25
|
const shouldOverflow = maxVisibleActions !== undefined && allActions.length > maxVisibleActions;
|
|
@@ -38,7 +39,7 @@ function MobileHeader({ title, subtitle, titleAlign = 'center', onBack, backLabe
|
|
|
38
39
|
};
|
|
39
40
|
return (jsxs("header", { className: buildClassName('w-full transition-colors select-none z-30', sticky && 'sticky top-0', transparent
|
|
40
41
|
? 'bg-white/80 dark:bg-gray-900/80 backdrop-blur-md border-b border-[var(--ui-border-muted)]'
|
|
41
|
-
: '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-
|
|
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-3 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: "flex items-center gap-1 min-w-[40px] shrink-0 z-10", "data-testid": "mobile-header-leading", children: onBack ? (jsxs("button", { type: "button", onClick: onBack, "aria-label": backLabel, className: "flex items-center gap-0.5 text-blue-600 dark:text-blue-400 hover:text-blue-700 p-1 -ml-1 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', hasLeadingSlot ? 'px-2' : 'pr-2', titleAlign === 'center'
|
|
42
43
|
? 'absolute inset-x-12 text-center pointer-events-none flex flex-col items-center justify-center'
|
|
43
44
|
: 'text-left'), children: [title && (jsx("div", { className: "text-sm font-bold text-gray-900 dark:text-white truncate pointer-events-auto", "data-testid": "mobile-header-title", children: title })), subtitle && (jsx("div", { className: "text-[11px] text-gray-500 dark:text-gray-400 truncate pointer-events-auto", "data-testid": "mobile-header-subtitle", children: subtitle }))] }), jsxs("div", { className: "flex items-center gap-1 min-w-[40px] shrink-0 justify-end z-10", children: [searchable && (jsx("button", { type: "button", onClick: () => setIsSearchActive(true), "aria-label": "Search", 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", "data-testid": "mobile-header-search-toggle", children: jsx(SearchIcon, {}) })), visibleActions.map((action) => {
|
|
44
45
|
const badgeColor = {
|
|
@@ -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\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-3.5 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 */}\n <div className=\"flex items-center gap-1 min-w-[40px] shrink-0 z-10\">\n {onBack ? (\n <button\n type=\"button\"\n onClick={onBack}\n aria-label={backLabel}\n className=\"flex items-center gap-0.5 text-blue-600 dark:text-blue-400 hover:text-blue-700 p-1 -ml-1 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 {/* Title & Subtitle Section */}\n <div\n className={buildClassName(\n 'flex-1 min-w-0 px-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":";;;;;AAKA,SAAS,eAAe,GAAA;AACtB,IAAA,QACEA,GAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,QAAQ,EAAC,IAAI,EAAC,MAAM,EAAC,OAAO,EAAC,WAAW,EAAC,MAAM,EAAC,cAAc,EAAA,QAAA,EAC3EA,GAAA,CAAA,MAAA,EAAA,EAAM,aAAa,EAAC,OAAO,EAAC,cAAc,EAAC,OAAO,EAAC,WAAW,EAAE,GAAG,EAAE,CAAC,EAAC,iBAAiB,EAAA,CAAG,EAAA,CACvF;AAEV;AAEA,SAAS,UAAU,GAAA;AACjB,IAAA,QACEA,GAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,sBAAsB,EAAC,IAAI,EAAC,MAAM,EAAC,OAAO,EAAC,WAAW,EAAC,MAAM,EAAC,cAAc,EAAA,QAAA,EACzFA,GAAA,CAAA,MAAA,EAAA,EACE,aAAa,EAAC,OAAO,EACrB,cAAc,EAAC,OAAO,EACtB,WAAW,EAAE,CAAC,EACd,CAAC,EAAC,6CAA6C,EAAA,CAC/C,EAAA,CACE;AAEV;AAEA,SAAS,QAAQ,GAAA;IACf,QACEC,IAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,QAAQ,EAAC,IAAI,EAAC,cAAc,EAAC,OAAO,EAAC,WAAW,EAAA,aAAA,EAAa,MAAM,EAAA,QAAA,EAAA,CAChFD,GAAA,CAAA,QAAA,EAAA,EAAQ,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,GAAG,EAAC,CAAC,EAAC,MAAM,EAAA,CAAG,EAClCA,GAAA,CAAA,QAAA,EAAA,EAAQ,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,IAAI,EAAC,CAAC,EAAC,MAAM,EAAA,CAAG,EACnCA,GAAA,CAAA,QAAA,EAAA,EAAQ,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,IAAI,EAAC,CAAC,EAAC,MAAM,EAAA,CAAG,CAAA,EAAA,CAC/B;AAEV;AAEA,SAAS,SAAS,GAAA;AAChB,IAAA,QACEA,GAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,QAAQ,EAAC,IAAI,EAAC,MAAM,EAAC,OAAO,EAAC,WAAW,EAAC,MAAM,EAAC,cAAc,EAAA,QAAA,EAC3EA,GAAA,CAAA,MAAA,EAAA,EAAM,aAAa,EAAC,OAAO,EAAC,cAAc,EAAC,OAAO,EAAC,WAAW,EAAE,CAAC,EAAE,CAAC,EAAC,sBAAsB,EAAA,CAAG,EAAA,CAC1F;AAEV;AAEM,SAAU,YAAY,CAAC,EAC3B,KAAK,EACL,QAAQ,EACR,UAAU,GAAG,QAAQ,EACrB,MAAM,EACN,SAAS,GAAG,MAAM,EAClB,OAAO,EACP,OAAO,EACP,iBAAiB,EACjB,aAAa,GAAG,cAAc,EAC9B,QAAQ,EACR,UAAU,GAAG,KAAK,EAClB,WAAW,EAAE,mBAAmB,EAChC,cAAc,EACd,iBAAiB,GAAG,WAAW,EAC/B,MAAM,GAAG,IAAI,EACb,WAAW,GAAG,KAAK,EACnB,UAAU,EACV,SAAS,GAAG,EAAE,GACI,EAAA;IAClB,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC;IAC3D,MAAM,CAAC,mBAAmB,EAAE,sBAAsB,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC;IAClE,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC;;IAG3D,MAAM,UAAU,GAAG,OAAO,KAAA,IAAA,IAAP,OAAO,KAAA,MAAA,GAAP,OAAO,GAAI,EAAE;IAChC,MAAM,cAAc,GAAG,iBAAiB,KAAK,SAAS,IAAI,UAAU,CAAC,MAAM,GAAG,iBAAiB;IAC/F,MAAM,cAAc,GAAG;AACrB,UAAE,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,iBAAiB,GAAG,CAAC,EAAE,CAAC,CAAC;UACtD,UAAU;AACd,IAAA,MAAM,eAAe,GAAG,cAAc,GAAG,UAAU,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,GAAG,EAAE;AAErF,IAAA,MAAM,WAAW,GAAG,mBAAmB,KAAK,SAAS,GAAG,mBAAmB,GAAG,mBAAmB;AAEjG,IAAA,MAAM,iBAAiB,GAAG,CAAC,CAAsC,KAAI;AACnE,QAAA,MAAM,GAAG,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK;QAC1B,sBAAsB,CAAC,GAAG,CAAC;AAC3B,QAAA,cAAc,aAAd,cAAc,KAAA,MAAA,GAAA,MAAA,GAAd,cAAc,CAAG,GAAG,CAAC;AACvB,IAAA,CAAC;IAED,MAAM,iBAAiB,GAAG,MAAK;QAC7B,sBAAsB,CAAC,EAAE,CAAC;AAC1B,QAAA,cAAc,aAAd,cAAc,KAAA,MAAA,GAAA,MAAA,GAAd,cAAc,CAAG,EAAE,CAAC;AACtB,IAAA,CAAC;AAED,IAAA,QACEC,IAAA,CAAA,QAAA,EAAA,EACE,SAAS,EAAE,cAAc,CACvB,2CAA2C,EAC3C,MAAM,IAAI,cAAc,EACxB;AACE,cAAE;AACF,cAAE,wEAAwE,EAC5E,mCAAmC,EACnC,SAAS,CACV,EAAA,aAAA,EACW,eAAe,EAAA,QAAA,EAAA,CAG3BD,GAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,8DAA8D,YAE1E,UAAU,IAAI,cAAc,IAC3BC,cACE,SAAS,EAAC,gCAAgC,EAAA,aAAA,EAC9B,gCAAgC,aAE5CA,IAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,mCAAmC,EAAA,QAAA,EAAA,CAChDD,cAAM,SAAS,EAAC,qCAAqC,EAAA,QAAA,EACnDA,GAAA,CAAC,UAAU,EAAA,EAAA,CAAG,EAAA,CACT,EACPA,GAAA,CAAA,OAAA,EAAA,EACE,IAAI,EAAC,MAAM,EACX,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,iBAAiB,EAC3B,WAAW,EAAE,iBAAiB,EAC9B,SAAS,EAAA,IAAA,EACT,SAAS,EAAC,6LAA6L,EAAA,aAAA,EAC3L,4BAA4B,GACxC,EACD,WAAW,KACVA,GAAA,CAAA,QAAA,EAAA,EACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,iBAAiB,EAAA,YAAA,EACf,cAAc,EACzB,SAAS,EAAC,oGAAoG,EAAA,QAAA,EAE9GA,GAAA,CAAC,SAAS,EAAA,EAAA,CAAG,GACN,CACV,CAAA,EAAA,CACG,EACNA,GAAA,CAAA,QAAA,EAAA,EACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,MAAM,iBAAiB,CAAC,KAAK,CAAC,EACvC,SAAS,EAAC,yIAAyI,uBAG5I,CAAA,EAAA,CACL,KAENC,IAAA,CAAAC,QAAA,EAAA,EAAA,QAAA,EAAA,CAEEF,GAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,oDAAoD,YAChE,MAAM,IACLC,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,MAAM,gBACH,SAAS,EACrB,SAAS,EAAC,sIAAsI,iBACpI,wBAAwB,EAAA,QAAA,EAAA,CAEpCD,IAAC,eAAe,EAAA,EAAA,CAAG,EACnBA,GAAA,CAAA,MAAA,EAAA,EAAM,SAAS,EAAC,wCAAwC,EAAA,QAAA,EAAE,SAAS,EAAA,CAAQ,IACpE,KAET,OAAO,CACR,EAAA,CACG,EAGNC,IAAA,CAAA,KAAA,EAAA,EACE,SAAS,EAAE,cAAc,CACvB,qBAAqB,EACrB,UAAU,KAAK;AACb,kCAAE;AACF,kCAAE,WAAW,CAChB,EAAA,QAAA,EAAA,CAEA,KAAK,KACJD,GAAA,CAAA,KAAA,EAAA,EACE,SAAS,EAAC,8EAA8E,EAAA,aAAA,EAC5E,qBAAqB,EAAA,QAAA,EAEhC,KAAK,GACF,CACP,EACA,QAAQ,KACPA,GAAA,CAAA,KAAA,EAAA,EACE,SAAS,EAAC,2EAA2E,iBACzE,wBAAwB,EAAA,QAAA,EAEnC,QAAQ,EAAA,CACL,CACP,CAAA,EAAA,CACG,EAGNC,IAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,gEAAgE,EAAA,QAAA,EAAA,CAC5E,UAAU,KACTD,GAAA,CAAA,QAAA,EAAA,EACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,MAAM,iBAAiB,CAAC,IAAI,CAAC,gBAC3B,QAAQ,EACnB,SAAS,EAAC,2HAA2H,EAAA,aAAA,EACzH,6BAA6B,EAAA,QAAA,EAEzCA,GAAA,CAAC,UAAU,EAAA,EAAA,CAAG,EAAA,CACP,CACV,EAEA,cAAc,CAAC,GAAG,CAAC,CAAC,MAA0B,KAAI;AACjD,oCAAA,MAAM,UAAU,GAAG;AACjB,wCAAA,MAAM,EAAE,uBAAuB;AAC/B,wCAAA,IAAI,EAAE,wBAAwB;AAC9B,wCAAA,OAAO,EAAE,2BAA2B;AACrC,qCAAA,CAAC,MAAM,CAAC,YAAY,IAAI,QAAQ,CAAC;AAElC,oCAAA,QACEC,IAAA,CAAA,QAAA,EAAA,EAEE,IAAI,EAAC,QAAQ,EACb,QAAQ,EAAE,MAAM,CAAC,QAAQ,EACzB,OAAO,EAAE,MAAM,CAAC,OAAO,EAAA,YAAA,EACX,MAAM,CAAC,KAAK,EACxB,SAAS,EAAE,cAAc,CACvB,oIAAoI,EACpI,MAAM,CAAC,QAAQ,IAAI,mDAAmD,CACvE,EAAA,aAAA,EACY,CAAA,qBAAA,EAAwB,MAAM,CAAC,EAAE,EAAE,EAAA,QAAA,EAAA,CAEhDD,GAAA,CAAA,MAAA,EAAA,EAAM,SAAS,EAAC,WAAW,YAAE,MAAM,CAAC,IAAI,EAAA,CAAQ,EAC/C,MAAM,CAAC,KAAK,KAAK,SAAS,IAAI,MAAM,CAAC,KAAK,KAAK,IAAI,KAClDA,GAAA,CAAA,MAAA,EAAA,EACE,SAAS,EAAE,cAAc,CACvB,+HAA+H,EAC/H,UAAU,CACX,EAAA,aAAA,EACY,CAAA,oBAAA,EAAuB,MAAM,CAAC,EAAE,EAAE,EAAA,QAAA,EAE9C,MAAM,CAAC,KAAK,EAAA,CACR,CACR,CAAA,EAAA,EAtBI,MAAM,CAAC,EAAE,CAuBP;AAEb,gCAAA,CAAC,CAAC,EAED,eAAe,CAAC,MAAM,GAAG,CAAC,KACzBA,GAAA,CAAA,QAAA,EAAA,EACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,MAAM,iBAAiB,CAAC,IAAI,CAAC,EAAA,YAAA,EAC1B,aAAa,EAAA,eAAA,EACX,QAAQ,mBACP,cAAc,EAC7B,SAAS,EAAC,2HAA2H,EAAA,aAAA,EACzH,wBAAwB,YAEpCA,GAAA,CAAC,QAAQ,KAAG,EAAA,CACL,CACV,EAEA,QAAQ,IACL,CAAA,EAAA,CACL,CACJ,GACG,EAEL,eAAe,CAAC,MAAM,GAAG,CAAC,KACzBA,GAAA,CAAC,WAAW,IACV,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,MAAM,iBAAiB,CAAC,KAAK,CAAC,EACvC,OAAO,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC,MAAM,MAAM;oBACxC,EAAE,EAAE,MAAM,CAAC,EAAE;oBACb,KAAK,EAAE,MAAM,CAAC,KAAK;oBACnB,IAAI,EAAE,MAAM,CAAC,IAAI;oBACjB,QAAQ,EAAE,MAAM,CAAC,QAAQ;oBACzB,OAAO,EAAE,MAAM,CAAC,OAAO;AACxB,iBAAA,CAAC,CAAC,EAAA,CACH,CACH,EAGA,UAAU,KACTA,GAAA,CAAA,KAAA,EAAA,EACE,SAAS,EAAC,oDAAoD,EAAA,aAAA,EAClD,2BAA2B,EAAA,QAAA,EAEtC,UAAU,GACP,CACP,CAAA,EAAA,CACM;AAEb;AAEA,YAAY,CAAC,WAAW,GAAG,cAAc;;;;"}
|
|
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=\"flex items-center gap-1 min-w-[40px] shrink-0 z-10\"\n data-testid=\"mobile-header-leading\"\n >\n {onBack ? (\n <button\n type=\"button\"\n onClick={onBack}\n aria-label={backLabel}\n className=\"flex items-center gap-0.5 text-blue-600 dark:text-blue-400 hover:text-blue-700 p-1 -ml-1 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 hasLeadingSlot ? '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":";;;;;AAKA,SAAS,eAAe,GAAA;AACtB,IAAA,QACEA,GAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,QAAQ,EAAC,IAAI,EAAC,MAAM,EAAC,OAAO,EAAC,WAAW,EAAC,MAAM,EAAC,cAAc,EAAA,QAAA,EAC3EA,GAAA,CAAA,MAAA,EAAA,EAAM,aAAa,EAAC,OAAO,EAAC,cAAc,EAAC,OAAO,EAAC,WAAW,EAAE,GAAG,EAAE,CAAC,EAAC,iBAAiB,EAAA,CAAG,EAAA,CACvF;AAEV;AAEA,SAAS,UAAU,GAAA;AACjB,IAAA,QACEA,GAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,sBAAsB,EAAC,IAAI,EAAC,MAAM,EAAC,OAAO,EAAC,WAAW,EAAC,MAAM,EAAC,cAAc,EAAA,QAAA,EACzFA,GAAA,CAAA,MAAA,EAAA,EACE,aAAa,EAAC,OAAO,EACrB,cAAc,EAAC,OAAO,EACtB,WAAW,EAAE,CAAC,EACd,CAAC,EAAC,6CAA6C,EAAA,CAC/C,EAAA,CACE;AAEV;AAEA,SAAS,QAAQ,GAAA;IACf,QACEC,IAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,QAAQ,EAAC,IAAI,EAAC,cAAc,EAAC,OAAO,EAAC,WAAW,EAAA,aAAA,EAAa,MAAM,EAAA,QAAA,EAAA,CAChFD,GAAA,CAAA,QAAA,EAAA,EAAQ,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,GAAG,EAAC,CAAC,EAAC,MAAM,EAAA,CAAG,EAClCA,GAAA,CAAA,QAAA,EAAA,EAAQ,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,IAAI,EAAC,CAAC,EAAC,MAAM,EAAA,CAAG,EACnCA,GAAA,CAAA,QAAA,EAAA,EAAQ,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,IAAI,EAAC,CAAC,EAAC,MAAM,EAAA,CAAG,CAAA,EAAA,CAC/B;AAEV;AAEA,SAAS,SAAS,GAAA;AAChB,IAAA,QACEA,GAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,QAAQ,EAAC,IAAI,EAAC,MAAM,EAAC,OAAO,EAAC,WAAW,EAAC,MAAM,EAAC,cAAc,EAAA,QAAA,EAC3EA,GAAA,CAAA,MAAA,EAAA,EAAM,aAAa,EAAC,OAAO,EAAC,cAAc,EAAC,OAAO,EAAC,WAAW,EAAE,CAAC,EAAE,CAAC,EAAC,sBAAsB,EAAA,CAAG,EAAA,CAC1F;AAEV;AAEM,SAAU,YAAY,CAAC,EAC3B,KAAK,EACL,QAAQ,EACR,UAAU,GAAG,QAAQ,EACrB,MAAM,EACN,SAAS,GAAG,MAAM,EAClB,OAAO,EACP,OAAO,EACP,iBAAiB,EACjB,aAAa,GAAG,cAAc,EAC9B,QAAQ,EACR,UAAU,GAAG,KAAK,EAClB,WAAW,EAAE,mBAAmB,EAChC,cAAc,EACd,iBAAiB,GAAG,WAAW,EAC/B,MAAM,GAAG,IAAI,EACb,WAAW,GAAG,KAAK,EACnB,UAAU,EACV,SAAS,GAAG,EAAE,GACI,EAAA;IAClB,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC;IAC3D,MAAM,CAAC,mBAAmB,EAAE,sBAAsB,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC;IAClE,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC;AAC3D,IAAA,MAAM,cAAc,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,IAAI,UAAU,KAAK,QAAQ;;IAG5E,MAAM,UAAU,GAAG,OAAO,KAAA,IAAA,IAAP,OAAO,KAAA,MAAA,GAAP,OAAO,GAAI,EAAE;IAChC,MAAM,cAAc,GAAG,iBAAiB,KAAK,SAAS,IAAI,UAAU,CAAC,MAAM,GAAG,iBAAiB;IAC/F,MAAM,cAAc,GAAG;AACrB,UAAE,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,iBAAiB,GAAG,CAAC,EAAE,CAAC,CAAC;UACtD,UAAU;AACd,IAAA,MAAM,eAAe,GAAG,cAAc,GAAG,UAAU,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,GAAG,EAAE;AAErF,IAAA,MAAM,WAAW,GAAG,mBAAmB,KAAK,SAAS,GAAG,mBAAmB,GAAG,mBAAmB;AAEjG,IAAA,MAAM,iBAAiB,GAAG,CAAC,CAAsC,KAAI;AACnE,QAAA,MAAM,GAAG,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK;QAC1B,sBAAsB,CAAC,GAAG,CAAC;AAC3B,QAAA,cAAc,aAAd,cAAc,KAAA,MAAA,GAAA,MAAA,GAAd,cAAc,CAAG,GAAG,CAAC;AACvB,IAAA,CAAC;IAED,MAAM,iBAAiB,GAAG,MAAK;QAC7B,sBAAsB,CAAC,EAAE,CAAC;AAC1B,QAAA,cAAc,aAAd,cAAc,KAAA,MAAA,GAAA,MAAA,GAAd,cAAc,CAAG,EAAE,CAAC;AACtB,IAAA,CAAC;AAED,IAAA,QACEC,IAAA,CAAA,QAAA,EAAA,EACE,SAAS,EAAE,cAAc,CACvB,2CAA2C,EAC3C,MAAM,IAAI,cAAc,EACxB;AACE,cAAE;cACA,wEAAwE,EAC5E,mCAAmC,EACnC,SAAS,CACV,iBACW,eAAe,EAAA,QAAA,EAAA,CAG3BD,aAAK,SAAS,EAAC,4DAA4D,EAAA,QAAA,EAExE,UAAU,IAAI,cAAc,IAC3BC,cACE,SAAS,EAAC,gCAAgC,EAAA,aAAA,EAC9B,gCAAgC,aAE5CA,IAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,mCAAmC,EAAA,QAAA,EAAA,CAChDD,cAAM,SAAS,EAAC,qCAAqC,EAAA,QAAA,EACnDA,GAAA,CAAC,UAAU,EAAA,EAAA,CAAG,EAAA,CACT,EACPA,GAAA,CAAA,OAAA,EAAA,EACE,IAAI,EAAC,MAAM,EACX,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,iBAAiB,EAC3B,WAAW,EAAE,iBAAiB,EAC9B,SAAS,QACT,SAAS,EAAC,6LAA6L,EAAA,aAAA,EAC3L,4BAA4B,EAAA,CACxC,EACD,WAAW,KACVA,GAAA,CAAA,QAAA,EAAA,EACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,iBAAiB,gBACf,cAAc,EACzB,SAAS,EAAC,oGAAoG,YAE9GA,GAAA,CAAC,SAAS,KAAG,EAAA,CACN,CACV,CAAA,EAAA,CACG,EACNA,GAAA,CAAA,QAAA,EAAA,EACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,MAAM,iBAAiB,CAAC,KAAK,CAAC,EACvC,SAAS,EAAC,yIAAyI,EAAA,QAAA,EAAA,QAAA,EAAA,CAG5I,CAAA,EAAA,CACL,KAENC,IAAA,CAAAC,QAAA,EAAA,EAAA,QAAA,EAAA,CAIG,cAAc,KACbF,aACE,SAAS,EAAC,oDAAoD,EAAA,aAAA,EAClD,uBAAuB,YAElC,MAAM,IACLC,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,MAAM,EAAA,YAAA,EACH,SAAS,EACrB,SAAS,EAAC,sIAAsI,EAAA,aAAA,EACpI,wBAAwB,aAEpCD,GAAA,CAAC,eAAe,KAAG,EACnBA,GAAA,CAAA,MAAA,EAAA,EAAM,SAAS,EAAC,wCAAwC,YAAE,SAAS,EAAA,CAAQ,IACpE,KAET,OAAO,CACR,EAAA,CACG,CACP,EAGDC,IAAA,CAAA,KAAA,EAAA,EACE,SAAS,EAAE,cAAc,CACvB,gBAAgB,EAChB,cAAc,GAAG,MAAM,GAAG,MAAM,EAChC,UAAU,KAAK;AACb,kCAAE;AACF,kCAAE,WAAW,CAChB,EAAA,QAAA,EAAA,CAEA,KAAK,KACJD,GAAA,CAAA,KAAA,EAAA,EACE,SAAS,EAAC,8EAA8E,EAAA,aAAA,EAC5E,qBAAqB,EAAA,QAAA,EAEhC,KAAK,GACF,CACP,EACA,QAAQ,KACPA,GAAA,CAAA,KAAA,EAAA,EACE,SAAS,EAAC,2EAA2E,iBACzE,wBAAwB,EAAA,QAAA,EAEnC,QAAQ,EAAA,CACL,CACP,CAAA,EAAA,CACG,EAGNC,IAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,gEAAgE,EAAA,QAAA,EAAA,CAC5E,UAAU,KACTD,GAAA,CAAA,QAAA,EAAA,EACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,MAAM,iBAAiB,CAAC,IAAI,CAAC,gBAC3B,QAAQ,EACnB,SAAS,EAAC,2HAA2H,EAAA,aAAA,EACzH,6BAA6B,EAAA,QAAA,EAEzCA,GAAA,CAAC,UAAU,EAAA,EAAA,CAAG,EAAA,CACP,CACV,EAEA,cAAc,CAAC,GAAG,CAAC,CAAC,MAA0B,KAAI;AACjD,oCAAA,MAAM,UAAU,GAAG;AACjB,wCAAA,MAAM,EAAE,uBAAuB;AAC/B,wCAAA,IAAI,EAAE,wBAAwB;AAC9B,wCAAA,OAAO,EAAE,2BAA2B;AACrC,qCAAA,CAAC,MAAM,CAAC,YAAY,IAAI,QAAQ,CAAC;AAElC,oCAAA,QACEC,IAAA,CAAA,QAAA,EAAA,EAEE,IAAI,EAAC,QAAQ,EACb,QAAQ,EAAE,MAAM,CAAC,QAAQ,EACzB,OAAO,EAAE,MAAM,CAAC,OAAO,EAAA,YAAA,EACX,MAAM,CAAC,KAAK,EACxB,SAAS,EAAE,cAAc,CACvB,oIAAoI,EACpI,MAAM,CAAC,QAAQ,IAAI,mDAAmD,CACvE,EAAA,aAAA,EACY,CAAA,qBAAA,EAAwB,MAAM,CAAC,EAAE,EAAE,EAAA,QAAA,EAAA,CAEhDD,GAAA,CAAA,MAAA,EAAA,EAAM,SAAS,EAAC,WAAW,YAAE,MAAM,CAAC,IAAI,EAAA,CAAQ,EAC/C,MAAM,CAAC,KAAK,KAAK,SAAS,IAAI,MAAM,CAAC,KAAK,KAAK,IAAI,KAClDA,GAAA,CAAA,MAAA,EAAA,EACE,SAAS,EAAE,cAAc,CACvB,+HAA+H,EAC/H,UAAU,CACX,EAAA,aAAA,EACY,CAAA,oBAAA,EAAuB,MAAM,CAAC,EAAE,EAAE,EAAA,QAAA,EAE9C,MAAM,CAAC,KAAK,EAAA,CACR,CACR,CAAA,EAAA,EAtBI,MAAM,CAAC,EAAE,CAuBP;AAEb,gCAAA,CAAC,CAAC,EAED,eAAe,CAAC,MAAM,GAAG,CAAC,KACzBA,GAAA,CAAA,QAAA,EAAA,EACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,MAAM,iBAAiB,CAAC,IAAI,CAAC,EAAA,YAAA,EAC1B,aAAa,EAAA,eAAA,EACX,QAAQ,mBACP,cAAc,EAC7B,SAAS,EAAC,2HAA2H,EAAA,aAAA,EACzH,wBAAwB,YAEpCA,GAAA,CAAC,QAAQ,KAAG,EAAA,CACL,CACV,EAEA,QAAQ,IACL,CAAA,EAAA,CACL,CACJ,GACG,EAEL,eAAe,CAAC,MAAM,GAAG,CAAC,KACzBA,GAAA,CAAC,WAAW,IACV,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,MAAM,iBAAiB,CAAC,KAAK,CAAC,EACvC,OAAO,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC,MAAM,MAAM;oBACxC,EAAE,EAAE,MAAM,CAAC,EAAE;oBACb,KAAK,EAAE,MAAM,CAAC,KAAK;oBACnB,IAAI,EAAE,MAAM,CAAC,IAAI;oBACjB,QAAQ,EAAE,MAAM,CAAC,QAAQ;oBACzB,OAAO,EAAE,MAAM,CAAC,OAAO;AACxB,iBAAA,CAAC,CAAC,EAAA,CACH,CACH,EAGA,UAAU,KACTA,GAAA,CAAA,KAAA,EAAA,EACE,SAAS,EAAC,oDAAoD,EAAA,aAAA,EAClD,2BAA2B,EAAA,QAAA,EAEtC,UAAU,GACP,CACP,CAAA,EAAA,CACM;AAEb;AAEA,YAAY,CAAC,WAAW,GAAG,cAAc;;;;"}
|
|
@@ -3495,6 +3495,7 @@ function MobileHeader({ title, subtitle, titleAlign = 'center', onBack, backLabe
|
|
|
3495
3495
|
const [isSearchActive, setIsSearchActive] = React.useState(false);
|
|
3496
3496
|
const [internalSearchValue, setInternalSearchValue] = React.useState('');
|
|
3497
3497
|
const [isOverflowOpen, setIsOverflowOpen] = React.useState(false);
|
|
3498
|
+
const hasLeadingSlot = Boolean(onBack || leading) || titleAlign === 'center';
|
|
3498
3499
|
// The overflow button takes one of the visible slots.
|
|
3499
3500
|
const allActions = actions !== null && actions !== void 0 ? actions : [];
|
|
3500
3501
|
const shouldOverflow = maxVisibleActions !== undefined && allActions.length > maxVisibleActions;
|
|
@@ -3514,7 +3515,7 @@ function MobileHeader({ title, subtitle, titleAlign = 'center', onBack, backLabe
|
|
|
3514
3515
|
};
|
|
3515
3516
|
return (jsxRuntime.jsxs("header", { className: buildClassName('w-full transition-colors select-none z-30', sticky && 'sticky top-0', transparent
|
|
3516
3517
|
? 'bg-white/80 dark:bg-gray-900/80 backdrop-blur-md border-b border-[var(--ui-border-muted)]'
|
|
3517
|
-
: '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-
|
|
3518
|
+
: '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-3 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$1, {}) }), 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: "flex items-center gap-1 min-w-[40px] shrink-0 z-10", "data-testid": "mobile-header-leading", children: onBack ? (jsxRuntime.jsxs("button", { type: "button", onClick: onBack, "aria-label": backLabel, className: "flex items-center gap-0.5 text-blue-600 dark:text-blue-400 hover:text-blue-700 p-1 -ml-1 rounded-xl transition-colors cursor-pointer", "data-testid": "mobile-header-back-btn", children: [jsxRuntime.jsx(ChevronLeftIcon$1, {}), jsxRuntime.jsx("span", { className: "text-xs font-semibold hidden sm:inline", children: backLabel })] })) : (leading) })), jsxRuntime.jsxs("div", { className: buildClassName('flex-1 min-w-0', hasLeadingSlot ? 'px-2' : 'pr-2', titleAlign === 'center'
|
|
3518
3519
|
? 'absolute inset-x-12 text-center pointer-events-none flex flex-col items-center justify-center'
|
|
3519
3520
|
: 'text-left'), children: [title && (jsxRuntime.jsx("div", { className: "text-sm font-bold text-gray-900 dark:text-white truncate pointer-events-auto", "data-testid": "mobile-header-title", children: title })), subtitle && (jsxRuntime.jsx("div", { className: "text-[11px] text-gray-500 dark:text-gray-400 truncate pointer-events-auto", "data-testid": "mobile-header-subtitle", children: subtitle }))] }), jsxRuntime.jsxs("div", { className: "flex items-center gap-1 min-w-[40px] shrink-0 justify-end z-10", children: [searchable && (jsxRuntime.jsx("button", { type: "button", onClick: () => setIsSearchActive(true), "aria-label": "Search", 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", "data-testid": "mobile-header-search-toggle", children: jsxRuntime.jsx(SearchIcon$1, {}) })), visibleActions.map((action) => {
|
|
3520
3521
|
const badgeColor = {
|