@pk-design/react-tailwind 1.8.1 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/Banner/Banner.js +1 -1
- package/dist/cjs/components/Banner/Banner.js.map +1 -1
- package/dist/cjs/components/BottomNavigation/BottomNavigation.js +14 -5
- package/dist/cjs/components/BottomNavigation/BottomNavigation.js.map +1 -1
- package/dist/cjs/components/Button/Button.js +88 -60
- package/dist/cjs/components/Button/Button.js.map +1 -1
- package/dist/cjs/components/Drawer/Drawer.js +6 -2
- package/dist/cjs/components/Drawer/Drawer.js.map +1 -1
- package/dist/cjs/components/MobileHeader/MobileHeader.js +21 -3
- package/dist/cjs/components/MobileHeader/MobileHeader.js.map +1 -1
- package/dist/cjs/components/PullToRefresh/PullToRefresh.js +145 -57
- package/dist/cjs/components/PullToRefresh/PullToRefresh.js.map +1 -1
- package/dist/cjs/components/SelectBox/SelectBox.js +25 -4
- package/dist/cjs/components/SelectBox/SelectBox.js.map +1 -1
- package/dist/cjs/components/StickyActionBar/StickyActionBar.js +2 -2
- package/dist/cjs/components/StickyActionBar/StickyActionBar.js.map +1 -1
- package/dist/cjs/components/Toast/Toast.js +1 -1
- package/dist/cjs/components/Toast/Toast.js.map +1 -1
- package/dist/cjs/hooks/use-breakpoint.js +48 -0
- package/dist/cjs/hooks/use-breakpoint.js.map +1 -0
- package/dist/cjs/index.js +5 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/es/components/Banner/Banner.js +1 -1
- package/dist/es/components/Banner/Banner.js.map +1 -1
- package/dist/es/components/BottomNavigation/BottomNavigation.js +14 -5
- package/dist/es/components/BottomNavigation/BottomNavigation.js.map +1 -1
- package/dist/es/components/Button/Button.js +89 -61
- package/dist/es/components/Button/Button.js.map +1 -1
- package/dist/es/components/Drawer/Drawer.js +6 -2
- package/dist/es/components/Drawer/Drawer.js.map +1 -1
- package/dist/es/components/MobileHeader/MobileHeader.js +21 -3
- package/dist/es/components/MobileHeader/MobileHeader.js.map +1 -1
- package/dist/es/components/PullToRefresh/PullToRefresh.js +146 -58
- package/dist/es/components/PullToRefresh/PullToRefresh.js.map +1 -1
- package/dist/es/components/SelectBox/SelectBox.js +26 -5
- package/dist/es/components/SelectBox/SelectBox.js.map +1 -1
- package/dist/es/components/StickyActionBar/StickyActionBar.js +2 -2
- package/dist/es/components/StickyActionBar/StickyActionBar.js.map +1 -1
- package/dist/es/components/Toast/Toast.js +1 -1
- package/dist/es/components/Toast/Toast.js.map +1 -1
- package/dist/es/hooks/use-breakpoint.js +43 -0
- package/dist/es/hooks/use-breakpoint.js.map +1 -0
- package/dist/es/index.js +1 -0
- package/dist/es/index.js.map +1 -1
- package/dist/react-tailwind.cjs.js +344 -135
- package/dist/react-tailwind.cjs.js.map +1 -1
- package/dist/react-tailwind.es.js +342 -137
- package/dist/react-tailwind.es.js.map +1 -1
- package/dist/types/components/BottomNavigation/BottomNavigation.d.ts +1 -1
- package/dist/types/components/BottomNavigation/BottomNavigation.types.d.ts +28 -3
- package/dist/types/components/Button/Button.types.d.ts +12 -6
- package/dist/types/components/Drawer/Drawer.types.d.ts +8 -4
- package/dist/types/components/MobileHeader/MobileHeader.d.ts +1 -1
- package/dist/types/components/MobileHeader/MobileHeader.types.d.ts +13 -0
- package/dist/types/components/PullToRefresh/PullToRefresh.d.ts +1 -1
- package/dist/types/components/PullToRefresh/PullToRefresh.types.d.ts +45 -6
- package/dist/types/components/SelectBox/SelectBox.types.d.ts +18 -1
- package/dist/types/components/StickyActionBar/StickyActionBar.types.d.ts +12 -0
- package/dist/types/hooks/use-breakpoint.d.ts +27 -0
- package/dist/types/hooks/use-breakpoint.test.d.ts +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/test-utils/mock-viewport.d.ts +7 -0
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
4
|
var React = require('react');
|
|
5
5
|
var buildClassname = require('../../utils/build-classname.js');
|
|
6
|
+
var ActionSheet = require('../ActionSheet/ActionSheet.js');
|
|
6
7
|
|
|
7
8
|
function ChevronLeftIcon() {
|
|
8
9
|
return (jsxRuntime.jsx("svg", { className: "size-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M15 19l-7-7 7-7" }) }));
|
|
@@ -10,12 +11,23 @@ function ChevronLeftIcon() {
|
|
|
10
11
|
function SearchIcon() {
|
|
11
12
|
return (jsxRuntime.jsx("svg", { className: "size-4 text-gray-400", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }));
|
|
12
13
|
}
|
|
14
|
+
function MoreIcon() {
|
|
15
|
+
return (jsxRuntime.jsxs("svg", { className: "size-5", fill: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: [jsxRuntime.jsx("circle", { cx: "12", cy: "5", r: "1.75" }), jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "1.75" }), jsxRuntime.jsx("circle", { cx: "12", cy: "19", r: "1.75" })] }));
|
|
16
|
+
}
|
|
13
17
|
function ClearIcon() {
|
|
14
18
|
return (jsxRuntime.jsx("svg", { className: "size-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }));
|
|
15
19
|
}
|
|
16
|
-
function MobileHeader({ title, subtitle, titleAlign = 'center', onBack, backLabel = 'Back', leading, actions, searchable = false, searchValue: externalSearchValue, onSearchChange, searchPlaceholder = 'Search...', sticky = true, transparent = false, bottomSlot, className = '', }) {
|
|
20
|
+
function MobileHeader({ title, subtitle, titleAlign = 'center', onBack, backLabel = 'Back', leading, actions, maxVisibleActions, overflowLabel = 'More actions', trailing, searchable = false, searchValue: externalSearchValue, onSearchChange, searchPlaceholder = 'Search...', sticky = true, transparent = false, bottomSlot, className = '', }) {
|
|
17
21
|
const [isSearchActive, setIsSearchActive] = React.useState(false);
|
|
18
22
|
const [internalSearchValue, setInternalSearchValue] = React.useState('');
|
|
23
|
+
const [isOverflowOpen, setIsOverflowOpen] = React.useState(false);
|
|
24
|
+
// The overflow button takes one of the visible slots.
|
|
25
|
+
const allActions = actions !== null && actions !== void 0 ? actions : [];
|
|
26
|
+
const shouldOverflow = maxVisibleActions !== undefined && allActions.length > maxVisibleActions;
|
|
27
|
+
const visibleActions = shouldOverflow
|
|
28
|
+
? allActions.slice(0, Math.max(maxVisibleActions - 1, 0))
|
|
29
|
+
: allActions;
|
|
30
|
+
const overflowActions = shouldOverflow ? allActions.slice(visibleActions.length) : [];
|
|
19
31
|
const searchValue = externalSearchValue !== undefined ? externalSearchValue : internalSearchValue;
|
|
20
32
|
const handleSearchInput = (e) => {
|
|
21
33
|
const val = e.target.value;
|
|
@@ -30,14 +42,20 @@ function MobileHeader({ title, subtitle, titleAlign = 'center', onBack, backLabe
|
|
|
30
42
|
? 'bg-white/80 dark:bg-gray-900/80 backdrop-blur-md border-b border-[var(--ui-border-muted)]'
|
|
31
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-3.5 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: [jsxRuntime.jsx("div", { className: "flex items-center gap-1 min-w-[40px] shrink-0 z-10", 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 px-2', titleAlign === 'center'
|
|
32
44
|
? 'absolute inset-x-12 text-center pointer-events-none flex flex-col items-center justify-center'
|
|
33
|
-
: '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, {}) })),
|
|
45
|
+
: '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) => {
|
|
34
46
|
const badgeColor = {
|
|
35
47
|
danger: 'bg-red-500 text-white',
|
|
36
48
|
info: 'bg-blue-500 text-white',
|
|
37
49
|
success: 'bg-emerald-500 text-white',
|
|
38
50
|
}[action.badgeVariant || 'danger'];
|
|
39
51
|
return (jsxRuntime.jsxs("button", { type: "button", disabled: action.disabled, onClick: action.onClick, "aria-label": action.label, className: buildClassname.buildClassName('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', action.disabled && 'opacity-40 cursor-not-allowed pointer-events-none'), "data-testid": `mobile-header-action-${action.id}`, children: [jsxRuntime.jsx("span", { className: "text-base", children: action.icon }), action.badge !== undefined && action.badge !== null && (jsxRuntime.jsx("span", { className: buildClassname.buildClassName('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', badgeColor), "data-testid": `mobile-header-badge-${action.id}`, children: action.badge }))] }, action.id));
|
|
40
|
-
})
|
|
52
|
+
}), overflowActions.length > 0 && (jsxRuntime.jsx("button", { type: "button", onClick: () => setIsOverflowOpen(true), "aria-label": overflowLabel, "aria-haspopup": "dialog", "aria-expanded": isOverflowOpen, 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-overflow", children: jsxRuntime.jsx(MoreIcon, {}) })), trailing] })] })) }), overflowActions.length > 0 && (jsxRuntime.jsx(ActionSheet.ActionSheet, { isOpen: isOverflowOpen, onClose: () => setIsOverflowOpen(false), actions: overflowActions.map((action) => ({
|
|
53
|
+
id: action.id,
|
|
54
|
+
label: action.label,
|
|
55
|
+
icon: action.icon,
|
|
56
|
+
disabled: action.disabled,
|
|
57
|
+
onClick: action.onClick,
|
|
58
|
+
})) })), bottomSlot && (jsxRuntime.jsx("div", { className: "px-3 py-2 border-t border-[var(--ui-border-muted)]", "data-testid": "mobile-header-bottom-slot", children: bottomSlot }))] }));
|
|
41
59
|
}
|
|
42
60
|
MobileHeader.displayName = 'MobileHeader';
|
|
43
61
|
|
|
@@ -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'\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 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 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\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 {actions?.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 </div>\n </>\n )}\n </div>\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","useState","_jsxs","buildClassName","_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\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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"}
|
|
@@ -5,77 +5,165 @@ var React = require('react');
|
|
|
5
5
|
var index = require('../Loader/index.js');
|
|
6
6
|
var buildClassname = require('../../utils/build-classname.js');
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
// Resistance applied to the finger's travel, so the pull feels elastic.
|
|
9
|
+
const RESISTANCE = 0.45;
|
|
10
|
+
// Finger travel (px) before deciding whether a gesture is a pull or a sideways swipe.
|
|
11
|
+
const AXIS_LOCK_SLOP = 8;
|
|
12
|
+
// Overlay badge size and the gap it rests at below the container's top edge.
|
|
13
|
+
const BADGE_SIZE = 40;
|
|
14
|
+
const BADGE_TOP_GAP = 10;
|
|
15
|
+
function PullArrowIcon({ progress, className }) {
|
|
16
|
+
return (jsxRuntime.jsx("svg", { className: className, style: { transform: `rotate(${Math.min(180, progress * 180)}deg)` }, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", "aria-hidden": "true", children: jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M19 14l-7 7m0 0l-7-7m7 7V3" }) }));
|
|
11
17
|
}
|
|
12
|
-
function PullToRefresh({ onRefresh, pullThreshold = 70, maxPullDistance = 120, disabled = false, refreshing: externalRefreshing, pullingContent, refreshingContent, children, className = '', contentClass = '', }) {
|
|
18
|
+
function PullToRefresh({ onRefresh, onError, variant = 'push', pullThreshold = 70, maxPullDistance = 120, disabled = false, refreshing: externalRefreshing, pullingContent, refreshingContent, pullLabel = 'Pull down to refresh', releaseLabel = 'Release to refresh', refreshingLabel = 'Refreshing...', refreshedLabel = 'Content refreshed', children, className = '', contentClass = '', }) {
|
|
13
19
|
const containerRef = React.useRef(null);
|
|
14
|
-
const startYRef = React.useRef(0);
|
|
15
|
-
const isPullingRef = React.useRef(false);
|
|
16
20
|
const [pullDistance, setPullDistance] = React.useState(0);
|
|
21
|
+
const [isDragging, setIsDragging] = React.useState(false);
|
|
17
22
|
const [internalRefreshing, setInternalRefreshing] = React.useState(false);
|
|
23
|
+
const [announcement, setAnnouncement] = React.useState('');
|
|
18
24
|
const isRefreshing = externalRefreshing !== undefined ? externalRefreshing : internalRefreshing;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
// The touch listeners are attached once, natively (see below), so they read
|
|
26
|
+
// the latest props and state through this ref instead of stale closures.
|
|
27
|
+
const latest = React.useRef({
|
|
28
|
+
disabled,
|
|
29
|
+
isRefreshing,
|
|
30
|
+
pullThreshold,
|
|
31
|
+
maxPullDistance,
|
|
32
|
+
onRefresh,
|
|
33
|
+
onError,
|
|
34
|
+
refreshedLabel,
|
|
35
|
+
});
|
|
36
|
+
React.useLayoutEffect(() => {
|
|
37
|
+
latest.current = {
|
|
38
|
+
disabled,
|
|
39
|
+
isRefreshing,
|
|
40
|
+
pullThreshold,
|
|
41
|
+
maxPullDistance,
|
|
42
|
+
onRefresh,
|
|
43
|
+
onError,
|
|
44
|
+
refreshedLabel,
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
React.useEffect(() => {
|
|
31
48
|
const container = containerRef.current;
|
|
32
|
-
if (!container
|
|
33
|
-
return;
|
|
34
|
-
const currentY = e.touches[0].clientY;
|
|
35
|
-
const rawDistance = currentY - startYRef.current;
|
|
36
|
-
if (rawDistance > 0) {
|
|
37
|
-
// Rubber-band resistance formula
|
|
38
|
-
const dampened = Math.min(maxPullDistance, rawDistance * 0.45);
|
|
39
|
-
setPullDistance(dampened);
|
|
40
|
-
if (e.cancelable) {
|
|
41
|
-
e.preventDefault();
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
setPullDistance(0);
|
|
46
|
-
}
|
|
47
|
-
}, [disabled, isRefreshing, maxPullDistance]);
|
|
48
|
-
const handleTouchEnd = React.useCallback(async () => {
|
|
49
|
-
if (!isPullingRef.current || disabled || isRefreshing)
|
|
49
|
+
if (!container)
|
|
50
50
|
return;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
let mounted = true;
|
|
52
|
+
let pulling = false;
|
|
53
|
+
let axis = 'pending';
|
|
54
|
+
let startX = 0;
|
|
55
|
+
let startY = 0;
|
|
56
|
+
let distance = 0;
|
|
57
|
+
const setDistance = (next) => {
|
|
58
|
+
distance = next;
|
|
59
|
+
setPullDistance(next);
|
|
60
|
+
};
|
|
61
|
+
// Ends a gesture without refreshing: a cancelled touch, a sideways swipe,
|
|
62
|
+
// or content that scrolled away from the top.
|
|
63
|
+
const reset = () => {
|
|
64
|
+
pulling = false;
|
|
65
|
+
setIsDragging(false);
|
|
66
|
+
setDistance(0);
|
|
67
|
+
};
|
|
68
|
+
const refresh = async () => {
|
|
69
|
+
const { pullThreshold: threshold, onRefresh: run, onError: report } = latest.current;
|
|
70
|
+
setDistance(threshold);
|
|
54
71
|
setInternalRefreshing(true);
|
|
55
72
|
try {
|
|
56
|
-
await
|
|
73
|
+
await run();
|
|
74
|
+
if (mounted)
|
|
75
|
+
setAnnouncement(latest.current.refreshedLabel);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
report === null || report === void 0 ? void 0 : report(error);
|
|
57
79
|
}
|
|
58
80
|
finally {
|
|
59
|
-
|
|
60
|
-
|
|
81
|
+
if (mounted) {
|
|
82
|
+
setInternalRefreshing(false);
|
|
83
|
+
setDistance(0);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
const handleTouchStart = (e) => {
|
|
88
|
+
var _a, _b;
|
|
89
|
+
const { disabled: isDisabled, isRefreshing: busy } = latest.current;
|
|
90
|
+
if (isDisabled || busy || container.scrollTop > 0 || e.touches.length !== 1)
|
|
91
|
+
return;
|
|
92
|
+
startX = (_a = e.touches[0].clientX) !== null && _a !== void 0 ? _a : 0;
|
|
93
|
+
startY = (_b = e.touches[0].clientY) !== null && _b !== void 0 ? _b : 0;
|
|
94
|
+
axis = 'pending';
|
|
95
|
+
pulling = true;
|
|
96
|
+
setAnnouncement('');
|
|
97
|
+
setIsDragging(true);
|
|
98
|
+
setDistance(0);
|
|
99
|
+
};
|
|
100
|
+
const handleTouchMove = (e) => {
|
|
101
|
+
var _a, _b, _c, _d;
|
|
102
|
+
if (!pulling)
|
|
103
|
+
return;
|
|
104
|
+
if (latest.current.disabled || latest.current.isRefreshing || container.scrollTop > 0) {
|
|
105
|
+
reset();
|
|
106
|
+
return;
|
|
61
107
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
108
|
+
const dx = ((_b = (_a = e.touches[0]) === null || _a === void 0 ? void 0 : _a.clientX) !== null && _b !== void 0 ? _b : 0) - startX;
|
|
109
|
+
const dy = ((_d = (_c = e.touches[0]) === null || _c === void 0 ? void 0 : _c.clientY) !== null && _d !== void 0 ? _d : 0) - startY;
|
|
110
|
+
if (axis === 'pending') {
|
|
111
|
+
if (Math.abs(dx) < AXIS_LOCK_SLOP && Math.abs(dy) < AXIS_LOCK_SLOP)
|
|
112
|
+
return;
|
|
113
|
+
axis = dy > 0 && Math.abs(dy) > Math.abs(dx) ? 'vertical' : 'horizontal';
|
|
114
|
+
// A sideways swipe or an upward scroll belongs to the content, not to us.
|
|
115
|
+
if (axis === 'horizontal') {
|
|
116
|
+
reset();
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const next = Math.min(latest.current.maxPullDistance, Math.max(0, dy) * RESISTANCE);
|
|
121
|
+
setDistance(next);
|
|
122
|
+
// Needs the non-passive listener: stops the browser's own pull-to-refresh/overscroll.
|
|
123
|
+
if (next > 0 && e.cancelable)
|
|
124
|
+
e.preventDefault();
|
|
125
|
+
};
|
|
126
|
+
const handleTouchEnd = () => {
|
|
127
|
+
if (!pulling)
|
|
128
|
+
return;
|
|
129
|
+
pulling = false;
|
|
130
|
+
setIsDragging(false);
|
|
131
|
+
if (distance >= latest.current.pullThreshold)
|
|
132
|
+
void refresh();
|
|
133
|
+
else
|
|
134
|
+
setDistance(0);
|
|
135
|
+
};
|
|
136
|
+
// React registers touch listeners as passive, where preventDefault() is
|
|
137
|
+
// ignored, so these are attached natively with touchmove non-passive.
|
|
138
|
+
container.addEventListener('touchstart', handleTouchStart, { passive: true });
|
|
139
|
+
container.addEventListener('touchmove', handleTouchMove, { passive: false });
|
|
140
|
+
container.addEventListener('touchend', handleTouchEnd);
|
|
141
|
+
container.addEventListener('touchcancel', reset);
|
|
142
|
+
return () => {
|
|
143
|
+
mounted = false;
|
|
144
|
+
container.removeEventListener('touchstart', handleTouchStart);
|
|
145
|
+
container.removeEventListener('touchmove', handleTouchMove);
|
|
146
|
+
container.removeEventListener('touchend', handleTouchEnd);
|
|
147
|
+
container.removeEventListener('touchcancel', reset);
|
|
148
|
+
};
|
|
149
|
+
}, []);
|
|
67
150
|
const progress = Math.min(1, pullDistance / pullThreshold);
|
|
68
151
|
const isReady = pullDistance >= pullThreshold;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
152
|
+
const offset = isRefreshing ? pullThreshold : pullDistance;
|
|
153
|
+
// Follow the finger exactly while dragging; animate only the settle afterwards.
|
|
154
|
+
const motionClass = isDragging
|
|
155
|
+
? 'transition-none'
|
|
156
|
+
: 'transition-transform duration-200 ease-out motion-reduce:transition-none';
|
|
157
|
+
return (jsxRuntime.jsxs("div", { ref: containerRef, className: buildClassname.buildClassName('relative overflow-y-auto overscroll-none', className), "data-testid": "pull-to-refresh-container", children: [jsxRuntime.jsx("span", { role: "status", "aria-live": "polite", className: "sr-only", children: isRefreshing ? refreshingLabel : announcement }), variant === 'overlay' ? (jsxRuntime.jsx("div", { className: buildClassname.buildClassName('pointer-events-none absolute inset-x-0 top-0 z-20 flex justify-center', motionClass), style: {
|
|
158
|
+
transform: `translateY(${offset - BADGE_SIZE + BADGE_TOP_GAP}px)`,
|
|
159
|
+
opacity: offset > 0 ? 1 : 0,
|
|
160
|
+
}, "aria-hidden": "true", "data-testid": "pull-to-refresh-indicator", children: jsxRuntime.jsx("div", { className: "grid size-10 place-items-center rounded-full bg-[var(--ui-primary)] text-white shadow-lg", children: isRefreshing
|
|
161
|
+
? (refreshingContent !== null && refreshingContent !== void 0 ? refreshingContent : jsxRuntime.jsx(index.Loader, { size: "xs", className: "text-white" }))
|
|
162
|
+
: (pullingContent !== null && pullingContent !== void 0 ? pullingContent : jsxRuntime.jsx(PullArrowIcon, { progress: progress, className: "size-4" })) }) })) : (
|
|
163
|
+
// Absolutely positioned, so the content's own translate is the only shift.
|
|
164
|
+
jsxRuntime.jsx("div", { className: "pointer-events-none absolute inset-x-0 top-0 z-20 flex items-center justify-center overflow-hidden", style: { height: `${offset}px`, opacity: offset > 0 ? 1 : 0 }, "aria-hidden": "true", "data-testid": "pull-to-refresh-indicator", children: jsxRuntime.jsx("div", { className: "flex items-center gap-2 px-3.5 py-1.5 rounded-full bg-white dark:bg-gray-800 border border-[var(--ui-border)] shadow-md text-xs font-semibold text-gray-700 dark:text-gray-200", children: isRefreshing
|
|
165
|
+
? (refreshingContent !== null && refreshingContent !== void 0 ? refreshingContent : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(index.Loader, { size: "sm" }), jsxRuntime.jsx("span", { children: refreshingLabel })] })))
|
|
166
|
+
: (pullingContent !== null && pullingContent !== void 0 ? pullingContent : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(PullArrowIcon, { progress: progress, className: "size-4 text-[var(--ui-primary)] dark:text-blue-400" }), jsxRuntime.jsx("span", { children: isReady ? releaseLabel : pullLabel })] }))) }) })), jsxRuntime.jsx("div", { className: buildClassname.buildClassName(variant === 'push' && motionClass, contentClass), style: variant === 'push' ? { transform: `translateY(${offset}px)` } : undefined, "data-testid": "pull-to-refresh-content", children: children })] }));
|
|
79
167
|
}
|
|
80
168
|
PullToRefresh.displayName = 'PullToRefresh';
|
|
81
169
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PullToRefresh.js","sources":["../../../../../src/components/PullToRefresh/PullToRefresh.tsx"],"sourcesContent":["import React, { useRef, useState, useCallback } from 'react'\nimport type { PullToRefreshProps } from './PullToRefresh.types'\nimport { Loader } from '../Loader'\nimport { buildClassName } from '../../utils/build-classname'\n\nfunction PullArrowIcon({ progress }: { progress: number }) {\n const rotation = Math.min(180, progress * 180)\n return (\n <svg\n className=\"size-4 text-blue-600 dark:text-blue-400 transition-transform duration-150\"\n style={{ transform: `rotate(${rotation}deg)` }}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2.5}\n d=\"M19 14l-7 7m0 0l-7-7m7 7V3\"\n />\n </svg>\n )\n}\n\nexport function PullToRefresh({\n onRefresh,\n pullThreshold = 70,\n maxPullDistance = 120,\n disabled = false,\n refreshing: externalRefreshing,\n pullingContent,\n refreshingContent,\n children,\n className = '',\n contentClass = '',\n}: PullToRefreshProps) {\n const containerRef = useRef<HTMLDivElement>(null)\n const startYRef = useRef<number>(0)\n const isPullingRef = useRef<boolean>(false)\n\n const [pullDistance, setPullDistance] = useState<number>(0)\n const [internalRefreshing, setInternalRefreshing] = useState<boolean>(false)\n\n const isRefreshing = externalRefreshing !== undefined ? externalRefreshing : internalRefreshing\n\n const handleTouchStart = useCallback(\n (e: React.TouchEvent<HTMLDivElement>) => {\n if (disabled || isRefreshing) return\n const container = containerRef.current\n if (container && container.scrollTop === 0) {\n startYRef.current = e.touches[0].clientY\n isPullingRef.current = true\n }\n },\n [disabled, isRefreshing],\n )\n\n const handleTouchMove = useCallback(\n (e: React.TouchEvent<HTMLDivElement>) => {\n if (!isPullingRef.current || disabled || isRefreshing) return\n const container = containerRef.current\n if (!container || container.scrollTop > 0) return\n\n const currentY = e.touches[0].clientY\n const rawDistance = currentY - startYRef.current\n\n if (rawDistance > 0) {\n // Rubber-band resistance formula\n const dampened = Math.min(maxPullDistance, rawDistance * 0.45)\n setPullDistance(dampened)\n if (e.cancelable) {\n e.preventDefault()\n }\n } else {\n setPullDistance(0)\n }\n },\n [disabled, isRefreshing, maxPullDistance],\n )\n\n const handleTouchEnd = useCallback(async () => {\n if (!isPullingRef.current || disabled || isRefreshing) return\n isPullingRef.current = false\n\n if (pullDistance >= pullThreshold) {\n setPullDistance(pullThreshold)\n setInternalRefreshing(true)\n try {\n await onRefresh()\n } finally {\n setInternalRefreshing(false)\n setPullDistance(0)\n }\n } else {\n setPullDistance(0)\n }\n }, [disabled, isRefreshing, onRefresh, pullDistance, pullThreshold])\n\n const progress = Math.min(1, pullDistance / pullThreshold)\n const isReady = pullDistance >= pullThreshold\n\n return (\n <div\n ref={containerRef}\n onTouchStart={handleTouchStart}\n onTouchMove={handleTouchMove}\n onTouchEnd={handleTouchEnd}\n className={buildClassName('relative overflow-y-auto overscroll-none', className)}\n data-testid=\"pull-to-refresh-container\"\n >\n {/* Pull / Refresh Indicator Header */}\n <div\n className=\"left-0 right-0 top-0 flex items-center justify-center pointer-events-none transition-all duration-150 z-20\"\n style={{\n height: isRefreshing ? `${pullThreshold}px` : `${pullDistance}px`,\n opacity: pullDistance > 0 || isRefreshing ? 1 : 0,\n }}\n data-testid=\"pull-to-refresh-indicator\"\n >\n <div className=\"flex items-center gap-2 px-3.5 py-1.5 rounded-full bg-white dark:bg-gray-800 border border-[var(--ui-border)] shadow-md text-xs font-semibold text-gray-700 dark:text-gray-200\">\n {isRefreshing ? (\n refreshingContent || (\n <>\n <Loader size=\"sm\" />\n <span>Refreshing...</span>\n </>\n )\n ) : pullingContent ? (\n pullingContent\n ) : (\n <>\n <PullArrowIcon progress={progress} />\n <span>{isReady ? 'Release to refresh' : 'Pull down to refresh'}</span>\n </>\n )}\n </div>\n </div>\n\n {/* Main Content */}\n <div\n className={buildClassName('transition-transform duration-150', contentClass)}\n style={{\n transform: isRefreshing\n ? `translateY(${pullThreshold}px)`\n : pullDistance > 0\n ? `translateY(${pullDistance}px)`\n : 'translateY(0px)',\n }}\n data-testid=\"pull-to-refresh-content\"\n >\n {children}\n </div>\n </div>\n )\n}\n\nPullToRefresh.displayName = 'PullToRefresh'\n"],"names":["_jsx","useRef","useState","useCallback","_jsxs","buildClassName","Loader","_Fragment"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PullToRefresh.js","sources":["../../../../../src/components/PullToRefresh/PullToRefresh.tsx"],"sourcesContent":["import React, { useEffect, useLayoutEffect, useRef, useState } from 'react'\nimport type { PullToRefreshProps } from './PullToRefresh.types'\nimport { Loader } from '../Loader'\nimport { buildClassName } from '../../utils/build-classname'\n\n// Resistance applied to the finger's travel, so the pull feels elastic.\nconst RESISTANCE = 0.45\n// Finger travel (px) before deciding whether a gesture is a pull or a sideways swipe.\nconst AXIS_LOCK_SLOP = 8\n// Overlay badge size and the gap it rests at below the container's top edge.\nconst BADGE_SIZE = 40\nconst BADGE_TOP_GAP = 10\n\nfunction PullArrowIcon({ progress, className }: { progress: number; className?: string }) {\n return (\n <svg\n className={className}\n style={{ transform: `rotate(${Math.min(180, progress * 180)}deg)` }}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2.5}\n d=\"M19 14l-7 7m0 0l-7-7m7 7V3\"\n />\n </svg>\n )\n}\n\ntype Axis = 'pending' | 'vertical' | 'horizontal'\n\nexport function PullToRefresh({\n onRefresh,\n onError,\n variant = 'push',\n pullThreshold = 70,\n maxPullDistance = 120,\n disabled = false,\n refreshing: externalRefreshing,\n pullingContent,\n refreshingContent,\n pullLabel = 'Pull down to refresh',\n releaseLabel = 'Release to refresh',\n refreshingLabel = 'Refreshing...',\n refreshedLabel = 'Content refreshed',\n children,\n className = '',\n contentClass = '',\n}: PullToRefreshProps) {\n const containerRef = useRef<HTMLDivElement>(null)\n const [pullDistance, setPullDistance] = useState(0)\n const [isDragging, setIsDragging] = useState(false)\n const [internalRefreshing, setInternalRefreshing] = useState(false)\n const [announcement, setAnnouncement] = useState('')\n\n const isRefreshing = externalRefreshing !== undefined ? externalRefreshing : internalRefreshing\n\n // The touch listeners are attached once, natively (see below), so they read\n // the latest props and state through this ref instead of stale closures.\n const latest = useRef({\n disabled,\n isRefreshing,\n pullThreshold,\n maxPullDistance,\n onRefresh,\n onError,\n refreshedLabel,\n })\n useLayoutEffect(() => {\n latest.current = {\n disabled,\n isRefreshing,\n pullThreshold,\n maxPullDistance,\n onRefresh,\n onError,\n refreshedLabel,\n }\n })\n\n useEffect(() => {\n const container = containerRef.current\n if (!container) return\n\n let mounted = true\n let pulling = false\n let axis: Axis = 'pending'\n let startX = 0\n let startY = 0\n let distance = 0\n\n const setDistance = (next: number) => {\n distance = next\n setPullDistance(next)\n }\n\n // Ends a gesture without refreshing: a cancelled touch, a sideways swipe,\n // or content that scrolled away from the top.\n const reset = () => {\n pulling = false\n setIsDragging(false)\n setDistance(0)\n }\n\n const refresh = async () => {\n const { pullThreshold: threshold, onRefresh: run, onError: report } = latest.current\n setDistance(threshold)\n setInternalRefreshing(true)\n try {\n await run()\n if (mounted) setAnnouncement(latest.current.refreshedLabel)\n } catch (error) {\n report?.(error)\n } finally {\n if (mounted) {\n setInternalRefreshing(false)\n setDistance(0)\n }\n }\n }\n\n const handleTouchStart = (e: TouchEvent) => {\n const { disabled: isDisabled, isRefreshing: busy } = latest.current\n if (isDisabled || busy || container.scrollTop > 0 || e.touches.length !== 1) return\n startX = e.touches[0].clientX ?? 0\n startY = e.touches[0].clientY ?? 0\n axis = 'pending'\n pulling = true\n setAnnouncement('')\n setIsDragging(true)\n setDistance(0)\n }\n\n const handleTouchMove = (e: TouchEvent) => {\n if (!pulling) return\n if (latest.current.disabled || latest.current.isRefreshing || container.scrollTop > 0) {\n reset()\n return\n }\n\n const dx = (e.touches[0]?.clientX ?? 0) - startX\n const dy = (e.touches[0]?.clientY ?? 0) - startY\n\n if (axis === 'pending') {\n if (Math.abs(dx) < AXIS_LOCK_SLOP && Math.abs(dy) < AXIS_LOCK_SLOP) return\n axis = dy > 0 && Math.abs(dy) > Math.abs(dx) ? 'vertical' : 'horizontal'\n // A sideways swipe or an upward scroll belongs to the content, not to us.\n if (axis === 'horizontal') {\n reset()\n return\n }\n }\n\n const next = Math.min(latest.current.maxPullDistance, Math.max(0, dy) * RESISTANCE)\n setDistance(next)\n // Needs the non-passive listener: stops the browser's own pull-to-refresh/overscroll.\n if (next > 0 && e.cancelable) e.preventDefault()\n }\n\n const handleTouchEnd = () => {\n if (!pulling) return\n pulling = false\n setIsDragging(false)\n if (distance >= latest.current.pullThreshold) void refresh()\n else setDistance(0)\n }\n\n // React registers touch listeners as passive, where preventDefault() is\n // ignored, so these are attached natively with touchmove non-passive.\n container.addEventListener('touchstart', handleTouchStart, { passive: true })\n container.addEventListener('touchmove', handleTouchMove, { passive: false })\n container.addEventListener('touchend', handleTouchEnd)\n container.addEventListener('touchcancel', reset)\n\n return () => {\n mounted = false\n container.removeEventListener('touchstart', handleTouchStart)\n container.removeEventListener('touchmove', handleTouchMove)\n container.removeEventListener('touchend', handleTouchEnd)\n container.removeEventListener('touchcancel', reset)\n }\n }, [])\n\n const progress = Math.min(1, pullDistance / pullThreshold)\n const isReady = pullDistance >= pullThreshold\n const offset = isRefreshing ? pullThreshold : pullDistance\n // Follow the finger exactly while dragging; animate only the settle afterwards.\n const motionClass = isDragging\n ? 'transition-none'\n : 'transition-transform duration-200 ease-out motion-reduce:transition-none'\n\n return (\n <div\n ref={containerRef}\n className={buildClassName('relative overflow-y-auto overscroll-none', className)}\n data-testid=\"pull-to-refresh-container\"\n >\n <span role=\"status\" aria-live=\"polite\" className=\"sr-only\">\n {isRefreshing ? refreshingLabel : announcement}\n </span>\n\n {variant === 'overlay' ? (\n <div\n className={buildClassName(\n 'pointer-events-none absolute inset-x-0 top-0 z-20 flex justify-center',\n motionClass,\n )}\n style={{\n transform: `translateY(${offset - BADGE_SIZE + BADGE_TOP_GAP}px)`,\n opacity: offset > 0 ? 1 : 0,\n }}\n aria-hidden=\"true\"\n data-testid=\"pull-to-refresh-indicator\"\n >\n <div className=\"grid size-10 place-items-center rounded-full bg-[var(--ui-primary)] text-white shadow-lg\">\n {isRefreshing\n ? (refreshingContent ?? <Loader size=\"xs\" className=\"text-white\" />)\n : (pullingContent ?? <PullArrowIcon progress={progress} className=\"size-4\" />)}\n </div>\n </div>\n ) : (\n // Absolutely positioned, so the content's own translate is the only shift.\n <div\n className=\"pointer-events-none absolute inset-x-0 top-0 z-20 flex items-center justify-center overflow-hidden\"\n style={{ height: `${offset}px`, opacity: offset > 0 ? 1 : 0 }}\n aria-hidden=\"true\"\n data-testid=\"pull-to-refresh-indicator\"\n >\n <div className=\"flex items-center gap-2 px-3.5 py-1.5 rounded-full bg-white dark:bg-gray-800 border border-[var(--ui-border)] shadow-md text-xs font-semibold text-gray-700 dark:text-gray-200\">\n {isRefreshing\n ? (refreshingContent ?? (\n <>\n <Loader size=\"sm\" />\n <span>{refreshingLabel}</span>\n </>\n ))\n : (pullingContent ?? (\n <>\n <PullArrowIcon\n progress={progress}\n className=\"size-4 text-[var(--ui-primary)] dark:text-blue-400\"\n />\n <span>{isReady ? releaseLabel : pullLabel}</span>\n </>\n ))}\n </div>\n </div>\n )}\n\n <div\n className={buildClassName(variant === 'push' && motionClass, contentClass)}\n style={variant === 'push' ? { transform: `translateY(${offset}px)` } : undefined}\n data-testid=\"pull-to-refresh-content\"\n >\n {children}\n </div>\n </div>\n )\n}\n\nPullToRefresh.displayName = 'PullToRefresh'\n"],"names":["_jsx","useRef","useState","useLayoutEffect","useEffect","_jsxs","buildClassName","Loader","_Fragment"],"mappings":";;;;;;;AAKA;AACA,MAAM,UAAU,GAAG,IAAI;AACvB;AACA,MAAM,cAAc,GAAG,CAAC;AACxB;AACA,MAAM,UAAU,GAAG,EAAE;AACrB,MAAM,aAAa,GAAG,EAAE;AAExB,SAAS,aAAa,CAAC,EAAE,QAAQ,EAAE,SAAS,EAA4C,EAAA;IACtF,QACEA,cAAA,CAAA,KAAA,EAAA,EACE,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,EAAE,SAAS,EAAE,CAAA,OAAA,EAAU,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,GAAG,GAAG,CAAC,CAAA,IAAA,CAAM,EAAE,EACnE,IAAI,EAAC,MAAM,EACX,OAAO,EAAC,WAAW,EACnB,MAAM,EAAC,cAAc,EAAA,aAAA,EACT,MAAM,EAAA,QAAA,EAElBA,cAAA,CAAA,MAAA,EAAA,EACE,aAAa,EAAC,OAAO,EACrB,cAAc,EAAC,OAAO,EACtB,WAAW,EAAE,GAAG,EAChB,CAAC,EAAC,4BAA4B,EAAA,CAC9B,EAAA,CACE;AAEV;AAIM,SAAU,aAAa,CAAC,EAC5B,SAAS,EACT,OAAO,EACP,OAAO,GAAG,MAAM,EAChB,aAAa,GAAG,EAAE,EAClB,eAAe,GAAG,GAAG,EACrB,QAAQ,GAAG,KAAK,EAChB,UAAU,EAAE,kBAAkB,EAC9B,cAAc,EACd,iBAAiB,EACjB,SAAS,GAAG,sBAAsB,EAClC,YAAY,GAAG,oBAAoB,EACnC,eAAe,GAAG,eAAe,EACjC,cAAc,GAAG,mBAAmB,EACpC,QAAQ,EACR,SAAS,GAAG,EAAE,EACd,YAAY,GAAG,EAAE,GACE,EAAA;AACnB,IAAA,MAAM,YAAY,GAAGC,YAAM,CAAiB,IAAI,CAAC;IACjD,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAGC,cAAQ,CAAC,CAAC,CAAC;IACnD,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAGA,cAAQ,CAAC,KAAK,CAAC;IACnD,MAAM,CAAC,kBAAkB,EAAE,qBAAqB,CAAC,GAAGA,cAAQ,CAAC,KAAK,CAAC;IACnE,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAGA,cAAQ,CAAC,EAAE,CAAC;AAEpD,IAAA,MAAM,YAAY,GAAG,kBAAkB,KAAK,SAAS,GAAG,kBAAkB,GAAG,kBAAkB;;;IAI/F,MAAM,MAAM,GAAGD,YAAM,CAAC;QACpB,QAAQ;QACR,YAAY;QACZ,aAAa;QACb,eAAe;QACf,SAAS;QACT,OAAO;QACP,cAAc;AACf,KAAA,CAAC;IACFE,qBAAe,CAAC,MAAK;QACnB,MAAM,CAAC,OAAO,GAAG;YACf,QAAQ;YACR,YAAY;YACZ,aAAa;YACb,eAAe;YACf,SAAS;YACT,OAAO;YACP,cAAc;SACf;AACH,IAAA,CAAC,CAAC;IAEFC,eAAS,CAAC,MAAK;AACb,QAAA,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO;AACtC,QAAA,IAAI,CAAC,SAAS;YAAE;QAEhB,IAAI,OAAO,GAAG,IAAI;QAClB,IAAI,OAAO,GAAG,KAAK;QACnB,IAAI,IAAI,GAAS,SAAS;QAC1B,IAAI,MAAM,GAAG,CAAC;QACd,IAAI,MAAM,GAAG,CAAC;QACd,IAAI,QAAQ,GAAG,CAAC;AAEhB,QAAA,MAAM,WAAW,GAAG,CAAC,IAAY,KAAI;YACnC,QAAQ,GAAG,IAAI;YACf,eAAe,CAAC,IAAI,CAAC;AACvB,QAAA,CAAC;;;QAID,MAAM,KAAK,GAAG,MAAK;YACjB,OAAO,GAAG,KAAK;YACf,aAAa,CAAC,KAAK,CAAC;YACpB,WAAW,CAAC,CAAC,CAAC;AAChB,QAAA,CAAC;AAED,QAAA,MAAM,OAAO,GAAG,YAAW;AACzB,YAAA,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,OAAO;YACpF,WAAW,CAAC,SAAS,CAAC;YACtB,qBAAqB,CAAC,IAAI,CAAC;AAC3B,YAAA,IAAI;gBACF,MAAM,GAAG,EAAE;AACX,gBAAA,IAAI,OAAO;AAAE,oBAAA,eAAe,CAAC,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC;YAC7D;YAAE,OAAO,KAAK,EAAE;AACd,gBAAA,MAAM,aAAN,MAAM,KAAA,MAAA,GAAA,MAAA,GAAN,MAAM,CAAG,KAAK,CAAC;YACjB;oBAAU;gBACR,IAAI,OAAO,EAAE;oBACX,qBAAqB,CAAC,KAAK,CAAC;oBAC5B,WAAW,CAAC,CAAC,CAAC;gBAChB;YACF;AACF,QAAA,CAAC;AAED,QAAA,MAAM,gBAAgB,GAAG,CAAC,CAAa,KAAI;;AACzC,YAAA,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,OAAO;AACnE,YAAA,IAAI,UAAU,IAAI,IAAI,IAAI,SAAS,CAAC,SAAS,GAAG,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;gBAAE;AAC7E,YAAA,MAAM,GAAG,CAAA,EAAA,GAAA,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,MAAA,IAAA,IAAA,EAAA,KAAA,MAAA,GAAA,EAAA,GAAI,CAAC;AAClC,YAAA,MAAM,GAAG,CAAA,EAAA,GAAA,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,MAAA,IAAA,IAAA,EAAA,KAAA,MAAA,GAAA,EAAA,GAAI,CAAC;YAClC,IAAI,GAAG,SAAS;YAChB,OAAO,GAAG,IAAI;YACd,eAAe,CAAC,EAAE,CAAC;YACnB,aAAa,CAAC,IAAI,CAAC;YACnB,WAAW,CAAC,CAAC,CAAC;AAChB,QAAA,CAAC;AAED,QAAA,MAAM,eAAe,GAAG,CAAC,CAAa,KAAI;;AACxC,YAAA,IAAI,CAAC,OAAO;gBAAE;AACd,YAAA,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,IAAI,MAAM,CAAC,OAAO,CAAC,YAAY,IAAI,SAAS,CAAC,SAAS,GAAG,CAAC,EAAE;AACrF,gBAAA,KAAK,EAAE;gBACP;YACF;AAEA,YAAA,MAAM,EAAE,GAAG,CAAC,MAAA,CAAA,EAAA,GAAA,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,0CAAE,OAAO,MAAA,IAAA,IAAA,EAAA,KAAA,MAAA,GAAA,EAAA,GAAI,CAAC,IAAI,MAAM;AAChD,YAAA,MAAM,EAAE,GAAG,CAAC,MAAA,CAAA,EAAA,GAAA,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,0CAAE,OAAO,MAAA,IAAA,IAAA,EAAA,KAAA,MAAA,GAAA,EAAA,GAAI,CAAC,IAAI,MAAM;AAEhD,YAAA,IAAI,IAAI,KAAK,SAAS,EAAE;AACtB,gBAAA,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,cAAc,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,cAAc;oBAAE;gBACpE,IAAI,GAAG,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,GAAG,YAAY;;AAExE,gBAAA,IAAI,IAAI,KAAK,YAAY,EAAE;AACzB,oBAAA,KAAK,EAAE;oBACP;gBACF;YACF;YAEA,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,eAAe,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,UAAU,CAAC;YACnF,WAAW,CAAC,IAAI,CAAC;;AAEjB,YAAA,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,UAAU;gBAAE,CAAC,CAAC,cAAc,EAAE;AAClD,QAAA,CAAC;QAED,MAAM,cAAc,GAAG,MAAK;AAC1B,YAAA,IAAI,CAAC,OAAO;gBAAE;YACd,OAAO,GAAG,KAAK;YACf,aAAa,CAAC,KAAK,CAAC;AACpB,YAAA,IAAI,QAAQ,IAAI,MAAM,CAAC,OAAO,CAAC,aAAa;gBAAE,KAAK,OAAO,EAAE;;gBACvD,WAAW,CAAC,CAAC,CAAC;AACrB,QAAA,CAAC;;;AAID,QAAA,SAAS,CAAC,gBAAgB,CAAC,YAAY,EAAE,gBAAgB,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC7E,QAAA,SAAS,CAAC,gBAAgB,CAAC,WAAW,EAAE,eAAe,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAC5E,QAAA,SAAS,CAAC,gBAAgB,CAAC,UAAU,EAAE,cAAc,CAAC;AACtD,QAAA,SAAS,CAAC,gBAAgB,CAAC,aAAa,EAAE,KAAK,CAAC;AAEhD,QAAA,OAAO,MAAK;YACV,OAAO,GAAG,KAAK;AACf,YAAA,SAAS,CAAC,mBAAmB,CAAC,YAAY,EAAE,gBAAgB,CAAC;AAC7D,YAAA,SAAS,CAAC,mBAAmB,CAAC,WAAW,EAAE,eAAe,CAAC;AAC3D,YAAA,SAAS,CAAC,mBAAmB,CAAC,UAAU,EAAE,cAAc,CAAC;AACzD,YAAA,SAAS,CAAC,mBAAmB,CAAC,aAAa,EAAE,KAAK,CAAC;AACrD,QAAA,CAAC;IACH,CAAC,EAAE,EAAE,CAAC;AAEN,IAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,YAAY,GAAG,aAAa,CAAC;AAC1D,IAAA,MAAM,OAAO,GAAG,YAAY,IAAI,aAAa;IAC7C,MAAM,MAAM,GAAG,YAAY,GAAG,aAAa,GAAG,YAAY;;IAE1D,MAAM,WAAW,GAAG;AAClB,UAAE;UACA,0EAA0E;IAE9E,QACEC,eAAA,CAAA,KAAA,EAAA,EACE,GAAG,EAAE,YAAY,EACjB,SAAS,EAAEC,6BAAc,CAAC,0CAA0C,EAAE,SAAS,CAAC,EAAA,aAAA,EACpE,2BAA2B,EAAA,QAAA,EAAA,CAEvCN,cAAA,CAAA,MAAA,EAAA,EAAM,IAAI,EAAC,QAAQ,EAAA,WAAA,EAAW,QAAQ,EAAC,SAAS,EAAC,SAAS,EAAA,QAAA,EACvD,YAAY,GAAG,eAAe,GAAG,YAAY,EAAA,CACzC,EAEN,OAAO,KAAK,SAAS,IACpBA,cAAA,CAAA,KAAA,EAAA,EACE,SAAS,EAAEM,6BAAc,CACvB,uEAAuE,EACvE,WAAW,CACZ,EACD,KAAK,EAAE;AACL,oBAAA,SAAS,EAAE,CAAA,WAAA,EAAc,MAAM,GAAG,UAAU,GAAG,aAAa,CAAA,GAAA,CAAK;oBACjE,OAAO,EAAE,MAAM,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC;iBAC5B,EAAA,aAAA,EACW,MAAM,iBACN,2BAA2B,EAAA,QAAA,EAEvCN,wBAAK,SAAS,EAAC,0FAA0F,EAAA,QAAA,EACtG;AACC,2BAAG,iBAAiB,aAAjB,iBAAiB,KAAA,MAAA,GAAjB,iBAAiB,GAAIA,cAAA,CAACO,YAAM,EAAA,EAAC,IAAI,EAAC,IAAI,EAAC,SAAS,EAAC,YAAY,GAAG;2BAChE,cAAc,KAAA,IAAA,IAAd,cAAc,cAAd,cAAc,GAAIP,cAAA,CAAC,aAAa,EAAA,EAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAC,QAAQ,EAAA,CAAG,CAAC,EAAA,CAC5E,EAAA,CACF;;AAGN,YAAAA,cAAA,CAAA,KAAA,EAAA,EACE,SAAS,EAAC,oGAAoG,EAC9G,KAAK,EAAE,EAAE,MAAM,EAAE,CAAA,EAAG,MAAM,CAAA,EAAA,CAAI,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAA,aAAA,EACjD,MAAM,EAAA,aAAA,EACN,2BAA2B,EAAA,QAAA,EAEvCA,cAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,gLAAgL,YAC5L;2BACI,iBAAiB,KAAA,IAAA,IAAjB,iBAAiB,KAAA,MAAA,GAAjB,iBAAiB,IAChBK,eAAA,CAAAG,mBAAA,EAAA,EAAA,QAAA,EAAA,CACER,cAAA,CAACO,YAAM,EAAA,EAAC,IAAI,EAAC,IAAI,EAAA,CAAG,EACpBP,mCAAO,eAAe,EAAA,CAAQ,CAAA,EAAA,CAC7B,CACJ;AACH,2BAAG,cAAc,KAAA,IAAA,IAAd,cAAc,cAAd,cAAc,IACbK,eAAA,CAAAG,mBAAA,EAAA,EAAA,QAAA,EAAA,CACER,eAAC,aAAa,EAAA,EACZ,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAC,oDAAoD,EAAA,CAC9D,EACFA,cAAA,CAAA,MAAA,EAAA,EAAA,QAAA,EAAO,OAAO,GAAG,YAAY,GAAG,SAAS,EAAA,CAAQ,CAAA,EAAA,CAChD,CACJ,CAAC,EAAA,CACF,GACF,CACP,EAEDA,cAAA,CAAA,KAAA,EAAA,EACE,SAAS,EAAEM,6BAAc,CAAC,OAAO,KAAK,MAAM,IAAI,WAAW,EAAE,YAAY,CAAC,EAC1E,KAAK,EAAE,OAAO,KAAK,MAAM,GAAG,EAAE,SAAS,EAAE,CAAA,WAAA,EAAc,MAAM,CAAA,GAAA,CAAK,EAAE,GAAG,SAAS,iBACpE,yBAAyB,EAAA,QAAA,EAEpC,QAAQ,EAAA,CACL,CAAA,EAAA,CACF;AAEV;AAEA,aAAa,CAAC,WAAW,GAAG,eAAe;;;;"}
|
|
@@ -43,9 +43,18 @@ const sizeClasses = {
|
|
|
43
43
|
},
|
|
44
44
|
};
|
|
45
45
|
const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
46
|
-
const { labelKey = 'label', valueKey = 'value', name, id = name, options = EMPTY_ARRAY, groups, selected, size = 'md', label = null, labelWrapperClass = '', labelClass = '', labelHint = null, disabled = false, containerClass = '', onChange = NOOP, placeholder, dropdownContainerClass = 'z-20', multiple = false, maxSelection, showSelectAll = false, immediate = true, showErrorMessage = true, error = null, helperText, leftGroup, renderOption, async = false, searchable = true, required = false, allowAdd = false, addNewText = 'Create', onAdd = NOOP, allowClear = false, allowFreeText = false, modalDropdown = false, noOptionsText = 'No options available', } = props;
|
|
46
|
+
const { labelKey = 'label', valueKey = 'value', name, id = name, options = EMPTY_ARRAY, groups, selected: selectedProp, defaultSelected, size = 'md', label = null, labelWrapperClass = '', labelClass = '', labelHint = null, disabled = false, containerClass = '', onChange: onChangeProp = NOOP, placeholder, dropdownContainerClass = 'z-20', dropdownMinWidth, multiple = false, maxSelection, showSelectAll = false, immediate = true, showErrorMessage = true, error = null, helperText, leftGroup, renderOption, async = false, searchable = true, required = false, allowAdd = false, addNewText = 'Create', onAdd = NOOP, allowClear = false, allowFreeText = false, modalDropdown = false, noOptionsText = 'No options available', } = props;
|
|
47
|
+
// Uncontrolled when `selected` is not passed: keep the selection internally.
|
|
48
|
+
const isControlled = selectedProp !== undefined;
|
|
49
|
+
const [internalSelected, setInternalSelected] = React.useState(defaultSelected !== null && defaultSelected !== void 0 ? defaultSelected : null);
|
|
50
|
+
const selected = isControlled ? selectedProp : internalSelected;
|
|
51
|
+
const onChange = React.useCallback((value) => {
|
|
52
|
+
if (!isControlled)
|
|
53
|
+
setInternalSelected(value);
|
|
54
|
+
onChangeProp(value);
|
|
55
|
+
}, [isControlled, onChangeProp]);
|
|
47
56
|
if (props.asBottomSheet || props.mobileMode === 'sheet') {
|
|
48
|
-
return jsxRuntime.jsx(MobilePicker.MobilePicker, { ...props });
|
|
57
|
+
return jsxRuntime.jsx(MobilePicker.MobilePicker, { ...props, selected: selected, onChange: onChange });
|
|
49
58
|
}
|
|
50
59
|
const hasError = Boolean(error);
|
|
51
60
|
const [query, setQuery] = React.useState('');
|
|
@@ -243,6 +252,16 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
|
243
252
|
}
|
|
244
253
|
}, [isAllSelected, onChange, allOptions]);
|
|
245
254
|
const hasLeftGroup = leftGroup != null && leftGroup !== undefined;
|
|
255
|
+
// Reserve room for the chevron (and the clear button when shown) so long values
|
|
256
|
+
// don't run underneath them.
|
|
257
|
+
const showClearButton = allowClear && !multiple && !isEmpty.isEmpty(normalizedSelected);
|
|
258
|
+
const rightPaddingClass = showClearButton
|
|
259
|
+
? 'pe-16'
|
|
260
|
+
: size === 'lg'
|
|
261
|
+
? 'pe-11'
|
|
262
|
+
: size === 'sm'
|
|
263
|
+
? 'pe-9'
|
|
264
|
+
: 'pe-10';
|
|
246
265
|
const hasFreeTextOption = Boolean(allowFreeText &&
|
|
247
266
|
query.trim() &&
|
|
248
267
|
!allOptions.some((opt) => { var _a; return ((_a = getDisplayValue(opt)) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === query.trim().toLowerCase(); }));
|
|
@@ -255,7 +274,7 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
|
255
274
|
const isOptSelected = selectedValueSet.has(optVal);
|
|
256
275
|
return (jsxRuntime.jsx(react.ComboboxOption, { disabled: option.disabled, value: option, className: "group rounded-lg py-1.5 px-3 select-none data-[focus]:bg-gray-100 dark:data-[focus]:bg-white/10 data-disabled:opacity-50 cursor-pointer data-disabled:pointer-events-none", children: renderOption ? (renderOption(option, Boolean(isOptSelected))) : (jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 text-gray-900 dark:text-white group-data-[selected]:text-blue-600", children: [jsxRuntime.jsx("span", { className: "text-sm/6", children: getDisplayValue(option) }), jsxRuntime.jsx(Check.default, { className: "invisible size-5 dark:stroke-gray-300 group-data-[selected]:visible" })] })) }, optVal));
|
|
257
276
|
};
|
|
258
|
-
return (jsxRuntime.jsxs("div", { className: buildClassname.buildClassName('group space-y-2', hasError && 'has-error', containerClass), ref: containerRef, children: [label && (jsxRuntime.jsxs("div", { className: buildClassname.buildClassName('flex items-center justify-between', labelWrapperClass), children: [jsxRuntime.jsx(Label.Label, { className: labelClass, htmlFor: id, "aria-required": required, children: label }), labelHint && jsxRuntime.jsx(TextContent.TextContent, { xsmall: true, children: labelHint })] })), jsxRuntime.jsxs(react.Combobox, { multiple: multiple, immediate: immediate, name: name, value: normalizedSelected, onChange: comboboxOnChangeWrapper, onClose: () => setQuery(''), disabled: disabled, by: (a, b) => (a && b ? getValue(a) === getValue(b) : a === b), children: [multiple ? (jsxRuntime.jsxs("div", { className: buildClassname.buildClassName(baseInputClass, 'flex flex-wrap gap-2 peer relative', hasLeftGroup ? (size === 'lg' ? 'ps-12' : size === 'sm' ? 'ps-8' : 'ps-10') : ''), ref: forwardedRef, children: [hasLeftGroup && (jsxRuntime.jsx("span", { className: buildClassname.buildClassName('absolute left-0 top-0 h-full flex items-center justify-center text-gray-400 dark:text-gray-500', currentSize.group), children: leftGroup })), isArray.isArray(normalizedSelected) &&
|
|
277
|
+
return (jsxRuntime.jsxs("div", { className: buildClassname.buildClassName('group space-y-2', hasError && 'has-error', containerClass), ref: containerRef, children: [label && (jsxRuntime.jsxs("div", { className: buildClassname.buildClassName('flex items-center justify-between', labelWrapperClass), children: [jsxRuntime.jsx(Label.Label, { className: labelClass, htmlFor: id, "aria-required": required, children: label }), labelHint && jsxRuntime.jsx(TextContent.TextContent, { xsmall: true, children: labelHint })] })), jsxRuntime.jsxs(react.Combobox, { multiple: multiple, immediate: immediate, name: name, value: normalizedSelected, onChange: comboboxOnChangeWrapper, onClose: () => setQuery(''), disabled: disabled, by: (a, b) => (a && b ? getValue(a) === getValue(b) : a === b), children: [multiple ? (jsxRuntime.jsxs("div", { className: buildClassname.buildClassName(baseInputClass, 'flex flex-wrap gap-2 peer relative', rightPaddingClass, hasLeftGroup ? (size === 'lg' ? 'ps-12' : size === 'sm' ? 'ps-8' : 'ps-10') : ''), ref: forwardedRef, children: [hasLeftGroup && (jsxRuntime.jsx("span", { className: buildClassname.buildClassName('absolute left-0 top-0 h-full flex items-center justify-center text-gray-400 dark:text-gray-500', currentSize.group), children: leftGroup })), isArray.isArray(normalizedSelected) &&
|
|
259
278
|
(normalizedSelected === null || normalizedSelected === void 0 ? void 0 : normalizedSelected.length) > 0 &&
|
|
260
279
|
normalizedSelected.map((option, idx) => (jsxRuntime.jsx(Badge.Badge, { theme: "secondary", size: currentSize.badgeSize, removable: !disabled, onRemove: () => removeSelection(idx), children: getDisplayValue(option) }, `${getValue(option)}-${idx}`))), jsxRuntime.jsx(react.ComboboxInput, { className: "outline-none flex-1 dark:placeholder-gray-400 placeholder-gray-400 bg-transparent", "aria-label": name, value: query, autoComplete: "off", placeholder: (normalizedSelected === null || normalizedSelected === void 0 ? void 0 : normalizedSelected.length) ? '' : placeholder, onChange: (e) => {
|
|
261
280
|
const val = e.target.value;
|
|
@@ -295,7 +314,9 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
|
295
314
|
}
|
|
296
315
|
}, onFocus: () => {
|
|
297
316
|
setQuery('');
|
|
298
|
-
}, className: buildClassname.buildClassName(baseInputClass, hasLeftGroup ? (size === 'lg' ? 'ps-12' : size === 'sm' ? 'ps-8' : 'ps-10') : ''), disabled: disabled, id: id, readOnly: !searchable, ref: inputRef, "data-testid": "combobox-input" }),
|
|
317
|
+
}, className: buildClassname.buildClassName(baseInputClass, 'text-ellipsis', rightPaddingClass, hasLeftGroup ? (size === 'lg' ? 'ps-12' : size === 'sm' ? 'ps-8' : 'ps-10') : ''), disabled: disabled, id: id, readOnly: !searchable, ref: inputRef, "data-testid": "combobox-input" }), showClearButton && (jsxRuntime.jsx(Button.Button, { size: "xs", iconOnly: true, "aria-label": "Clear selection", theme: "secondary", variant: "plain", className: "group absolute inset-y-0 right-8 h-6 top-2", onClick: () => handleOnChangeInternal(multiple ? [] : null), disabled: disabled, "data-testid": "combobox-clear-button", children: jsxRuntime.jsx(X.default, { className: "size-4 stroke-gray-500 dark:stroke-gray-300" }) })), jsxRuntime.jsx(react.ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5 flex items-center justify-center", "data-testid": "combobox-button", children: jsxRuntime.jsx(ChevronDown.default, { className: chevronIconClass }) })] })), jsxRuntime.jsxs(react.ComboboxOptions, { anchor: { to: 'bottom start', gap: 5, padding: 8 }, as: "div", modal: modalDropdown,
|
|
318
|
+
// Matches the input width by default; min-width lets the panel grow wider than a narrow input.
|
|
319
|
+
style: dropdownMinWidth != null ? { minWidth: dropdownMinWidth } : undefined, className: buildClassname.buildClassName('w-[var(--input-width)] rounded-lg border border-[var(--ui-border)] bg-white dark:bg-gray-800 p-1 shadow-lg max-h-80 overflow-y-auto z-50 [--anchor-gap:var(--spacing-1)]', '[--anchor-max-height:20rem]', 'origin-top border transition duration-200 ease-[cubic-bezier(0.4, 0.0, 0.2, 1)] data-[closed]:translate-y-[-15%] data-[closed]:opacity-0', dropdownContainerClass), transition: true, "data-testid": "combobox-options", children: [multiple && showSelectAll && allOptions.length > 0 && (jsxRuntime.jsxs("div", { className: "pb-1 mb-1 border-b border-[var(--ui-border-muted)] px-3 pt-1 flex items-center justify-between", children: [jsxRuntime.jsx("button", { type: "button", onClick: handleToggleSelectAll, className: "text-xs font-semibold text-blue-600 dark:text-blue-400 hover:underline cursor-pointer", children: isAllSelected ? 'Deselect All' : 'Select All' }), jsxRuntime.jsxs("span", { className: "text-[11px] font-medium text-gray-400", children: [Array.isArray(normalizedSelected) ? normalizedSelected.length : 0, " of", ' ', allOptions.length] })] })), optionsLoading ? (jsxRuntime.jsx(BodyText.BodyText, { "data-testid": "options-loading", className: "flex items-center justify-center py-2 px-3 select-none", children: jsxRuntime.jsx(index.Loader, { size: "sm" }) })) : shouldShowTypeToSearch ? (jsxRuntime.jsx(BodyText.BodyText, { className: "flex items-center justify-between py-2 px-3 select-none", children: "Type to search..." })) : (jsxRuntime.jsxs("div", { children: [allowFreeText &&
|
|
299
320
|
query.trim() &&
|
|
300
321
|
!allOptions.some((opt) => { var _a; return ((_a = getDisplayValue(opt)) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === query.trim().toLowerCase(); }) && (jsxRuntime.jsx(react.ComboboxOption, { value: createFreeTextOption(query.trim()), className: "group flex items-center gap-2 rounded-lg py-1.5 px-3 select-none text-sm data-[focus]:bg-blue-50 dark:data-[focus]:bg-blue-950/40 text-blue-600 dark:text-blue-400 font-medium cursor-pointer w-full", "data-testid": "freetext-option", children: jsxRuntime.jsxs("span", { children: ["Use \"", query.trim(), "\""] }) }, `freetext-${query.trim()}`)), filteredGroups
|
|
301
322
|
? filteredGroups.map((grp, gIdx) => (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("div", { className: "text-[11px] font-bold uppercase tracking-wider text-gray-400 dark:text-gray-500 px-3 py-1 select-none", children: grp.group }), grp.options.map(renderOptionItem)] }, gIdx)))
|