Exersice name field focus opens keyboard. Typing filters the options.
This commit is contained in:
@@ -4,15 +4,18 @@ interface FilledInputProps {
|
||||
label: string;
|
||||
value: string | number;
|
||||
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
onFocus?: (e: React.FocusEvent<HTMLInputElement>) => void;
|
||||
onBlur?: (e: React.FocusEvent<HTMLInputElement>) => void;
|
||||
type?: string;
|
||||
icon?: React.ReactNode;
|
||||
autoFocus?: boolean;
|
||||
step?: string;
|
||||
inputMode?: "search" | "text" | "email" | "tel" | "url" | "none" | "numeric" | "decimal";
|
||||
autocapitalize?: "off" | "none" | "on" | "sentences" | "words" | "characters";
|
||||
autoComplete?: string;
|
||||
}
|
||||
|
||||
const FilledInput: React.FC<FilledInputProps> = ({ label, value, onChange, type = "number", icon, autoFocus, step, inputMode, autocapitalize }) => (
|
||||
const FilledInput: React.FC<FilledInputProps> = ({ label, value, onChange, onFocus, onBlur, type = "number", icon, autoFocus, step, inputMode, autocapitalize, autoComplete }) => (
|
||||
<div className="relative group bg-surface-container-high rounded-t-lg border-b border-outline-variant hover:bg-white/5 focus-within:border-primary transition-colors">
|
||||
<label className="absolute top-2 left-4 text-[10px] font-medium text-on-surface-variant flex items-center gap-1">
|
||||
{icon} {label}
|
||||
@@ -26,7 +29,10 @@ const FilledInput: React.FC<FilledInputProps> = ({ label, value, onChange, type
|
||||
placeholder="0"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
autoCapitalize={autocapitalize}
|
||||
autoComplete={autoComplete}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -30,6 +30,8 @@ const Tracker: React.FC<TrackerProps> = ({ userId, userWeight, activeSession, ac
|
||||
const [plans, setPlans] = useState<WorkoutPlan[]>([]);
|
||||
const [selectedExercise, setSelectedExercise] = useState<ExerciseDef | null>(null);
|
||||
const [lastSet, setLastSet] = useState<WorkoutSet | undefined>(undefined);
|
||||
const [searchQuery, setSearchQuery] = useState<string>('');
|
||||
const [showSuggestions, setShowSuggestions] = useState(false);
|
||||
|
||||
// Timer State
|
||||
const [elapsedTime, setElapsedTime] = useState<string>('00:00:00');
|
||||
@@ -174,11 +176,19 @@ const Tracker: React.FC<TrackerProps> = ({ userId, userWeight, activeSession, ac
|
||||
if (selectedExercise.type !== ExerciseType.HIGH_JUMP) {
|
||||
setHeight('');
|
||||
}
|
||||
} else {
|
||||
setSearchQuery(''); // Clear search query if no exercise is selected
|
||||
}
|
||||
};
|
||||
updateSelection();
|
||||
}, [selectedExercise, userId]);
|
||||
|
||||
const filteredExercises = searchQuery === ''
|
||||
? exercises
|
||||
: exercises.filter(ex =>
|
||||
ex.name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
);
|
||||
|
||||
const handleStart = (plan?: WorkoutPlan) => {
|
||||
if (plan && plan.description) {
|
||||
setShowPlanPrep(plan);
|
||||
@@ -486,25 +496,46 @@ const Tracker: React.FC<TrackerProps> = ({ userId, userWeight, activeSession, ac
|
||||
<div className="flex-1 overflow-y-auto p-4 pb-32 space-y-6">
|
||||
|
||||
<div className="relative">
|
||||
<select
|
||||
className="w-full p-4 pr-12 bg-transparent border border-outline rounded-lg text-on-surface appearance-none focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary text-lg font-normal"
|
||||
value={selectedExercise?.id || ''}
|
||||
onChange={(e) => setSelectedExercise(exercises.find(ex => ex.id === e.target.value) || null)}
|
||||
>
|
||||
<option value="" disabled>{t('select_exercise', lang)}</option>
|
||||
{exercises
|
||||
.map(ex => (
|
||||
<option key={ex.id} value={ex.id} className="bg-surface-container text-on-surface">{ex.name}</option>
|
||||
))}
|
||||
</select>
|
||||
<ChevronDown className="absolute right-4 top-1/2 -translate-y-1/2 text-on-surface-variant pointer-events-none" size={24} />
|
||||
|
||||
<FilledInput
|
||||
label={t('select_exercise', lang)}
|
||||
value={searchQuery}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSearchQuery(e.target.value);
|
||||
setShowSuggestions(true);
|
||||
}}
|
||||
onFocus={() => setShowSuggestions(true)}
|
||||
onBlur={() => setTimeout(() => setShowSuggestions(false), 100)} // Delay hiding to allow click
|
||||
icon={<Dumbbell size={10} />}
|
||||
autoComplete="off"
|
||||
type="text"
|
||||
/>
|
||||
<button
|
||||
onClick={() => setIsCreating(true)}
|
||||
className="absolute right-12 top-1/2 -translate-y-1/2 p-2 text-primary hover:bg-primary-container/20 rounded-full"
|
||||
className="absolute right-2 top-1/2 -translate-y-1/2 p-2 text-primary hover:bg-primary-container/20 rounded-full z-10"
|
||||
>
|
||||
<Plus size={24} />
|
||||
</button>
|
||||
{showSuggestions && (
|
||||
<div className="absolute top-full left-0 w-full bg-surface-container rounded-xl shadow-elevation-3 overflow-hidden z-20 mt-1 max-h-60 overflow-y-auto animate-in fade-in slide-in-from-top-2">
|
||||
{filteredExercises.length > 0 ? (
|
||||
filteredExercises.map(ex => (
|
||||
<button
|
||||
key={ex.id}
|
||||
onClick={() => {
|
||||
setSelectedExercise(ex);
|
||||
setSearchQuery(ex.name);
|
||||
setShowSuggestions(false);
|
||||
}}
|
||||
className="w-full text-left px-4 py-3 text-on-surface hover:bg-surface-container-high transition-colors text-lg"
|
||||
>
|
||||
{ex.name}
|
||||
</button>
|
||||
))
|
||||
) : (
|
||||
<div className="px-4 py-3 text-on-surface-variant text-lg">{t('no_exercises_found', lang)}</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{selectedExercise && (
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user