fix(ui): adjust dropdown positioning and scrolling in voices control and improve the ui label for speed controls
This commit is contained in:
parent
c345628200
commit
fb8fc41994
2 changed files with 34 additions and 4 deletions
|
|
@ -45,7 +45,36 @@ export const SpeedControl = ({
|
||||||
}
|
}
|
||||||
}, [localAudioSpeed, audioPlayerSpeed, setAudioPlayerSpeedAndRestart]);
|
}, [localAudioSpeed, audioPlayerSpeed, setAudioPlayerSpeedAndRestart]);
|
||||||
|
|
||||||
const displaySpeed = useMemo(() => (localAudioSpeed !== 1.0 ? localAudioSpeed : localVoiceSpeed), [localAudioSpeed, localVoiceSpeed]);
|
const formatSpeed = useCallback((speed: number, maxDecimals: number) => {
|
||||||
|
const rounded = Number(speed.toFixed(maxDecimals));
|
||||||
|
return rounded.toString();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const triggerLabel = useMemo(
|
||||||
|
() => {
|
||||||
|
const parts: string[] = [];
|
||||||
|
if (localVoiceSpeed !== 1.0) parts.push(`${formatSpeed(localVoiceSpeed, 1)}x`);
|
||||||
|
if (localAudioSpeed !== 1.0) parts.push(`${formatSpeed(localAudioSpeed, 1)}x`);
|
||||||
|
return parts.length > 0 ? parts.join(' • ') : '1x';
|
||||||
|
},
|
||||||
|
[formatSpeed, localVoiceSpeed, localAudioSpeed]
|
||||||
|
);
|
||||||
|
|
||||||
|
const compactTriggerLabel = useMemo(() => {
|
||||||
|
const voiceIsDefault = localVoiceSpeed === 1.0;
|
||||||
|
const audioIsDefault = localAudioSpeed === 1.0;
|
||||||
|
|
||||||
|
let combined = 1.0;
|
||||||
|
if (!voiceIsDefault && !audioIsDefault) {
|
||||||
|
combined = (localVoiceSpeed + localAudioSpeed) / 2;
|
||||||
|
} else if (!voiceIsDefault) {
|
||||||
|
combined = localVoiceSpeed;
|
||||||
|
} else if (!audioIsDefault) {
|
||||||
|
combined = localAudioSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${formatSpeed(combined, 2)}x`;
|
||||||
|
}, [formatSpeed, localVoiceSpeed, localAudioSpeed]);
|
||||||
|
|
||||||
const min = 0.5;
|
const min = 0.5;
|
||||||
const max = 3;
|
const max = 3;
|
||||||
|
|
@ -55,7 +84,8 @@ export const SpeedControl = ({
|
||||||
<Popover className="relative">
|
<Popover className="relative">
|
||||||
<PopoverButton className="flex items-center space-x-0.5 sm:space-x-1 bg-transparent text-foreground text-xs sm:text-sm focus:outline-none cursor-pointer hover:bg-offbase rounded pl-1.5 sm:pl-2 pr-0.5 sm:pr-1 py-0.5 sm:py-1 transform transition-transform duration-200 ease-in-out hover:scale-[1.04] hover:text-accent">
|
<PopoverButton className="flex items-center space-x-0.5 sm:space-x-1 bg-transparent text-foreground text-xs sm:text-sm focus:outline-none cursor-pointer hover:bg-offbase rounded pl-1.5 sm:pl-2 pr-0.5 sm:pr-1 py-0.5 sm:py-1 transform transition-transform duration-200 ease-in-out hover:scale-[1.04] hover:text-accent">
|
||||||
<SpeedometerIcon className="h-3 w-3 sm:h-3.5 sm:w-3.5" />
|
<SpeedometerIcon className="h-3 w-3 sm:h-3.5 sm:w-3.5" />
|
||||||
<span>{Number.isInteger(displaySpeed) ? displaySpeed.toString() : displaySpeed.toFixed(1)}x</span>
|
<span className="sm:hidden">{compactTriggerLabel}</span>
|
||||||
|
<span className="hidden sm:inline">{triggerLabel}</span>
|
||||||
<ChevronUpDownIcon className="h-2.5 w-2.5 sm:h-3 sm:w-3" />
|
<ChevronUpDownIcon className="h-2.5 w-2.5 sm:h-3 sm:w-3" />
|
||||||
</PopoverButton>
|
</PopoverButton>
|
||||||
<PopoverPanel anchor="top" className="absolute z-50 bg-base p-3 rounded-md shadow-lg border border-offbase">
|
<PopoverPanel anchor="top" className="absolute z-50 bg-base p-3 rounded-md shadow-lg border border-offbase">
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ export function VoicesControlBase({
|
||||||
<span>{selectedVoices.length > 1 ? selectedVoices.join(' + ') : selectedVoices[0] || currentVoice}</span>
|
<span>{selectedVoices.length > 1 ? selectedVoices.join(' + ') : selectedVoices[0] || currentVoice}</span>
|
||||||
<ChevronUpDownIcon className="h-2.5 w-2.5 sm:h-3 sm:w-3" />
|
<ChevronUpDownIcon className="h-2.5 w-2.5 sm:h-3 sm:w-3" />
|
||||||
</ListboxButton>
|
</ListboxButton>
|
||||||
<ListboxOptions anchor="top end" className="absolute z-50 w-40 sm:w-44 max-h-64 overflow-auto rounded-lg bg-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
|
<ListboxOptions className="absolute bottom-full right-0 mb-1 z-50 w-40 sm:w-44 !h-auto !min-h-0 !max-h-[50vh] overflow-y-auto overscroll-contain rounded-lg bg-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
|
||||||
{availableVoices.map((voiceId) => (
|
{availableVoices.map((voiceId) => (
|
||||||
<ListboxOption
|
<ListboxOption
|
||||||
key={voiceId}
|
key={voiceId}
|
||||||
|
|
@ -117,7 +117,7 @@ export function VoicesControlBase({
|
||||||
<span>{currentVoice}</span>
|
<span>{currentVoice}</span>
|
||||||
<ChevronUpDownIcon className="h-2.5 w-2.5 sm:h-3 sm:w-3" />
|
<ChevronUpDownIcon className="h-2.5 w-2.5 sm:h-3 sm:w-3" />
|
||||||
</ListboxButton>
|
</ListboxButton>
|
||||||
<ListboxOptions anchor="top end" className="absolute z-50 w-28 sm:w-32 max-h-64 overflow-auto rounded-lg bg-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
|
<ListboxOptions className="absolute bottom-full right-0 mb-1 z-50 w-28 sm:w-32 !h-auto !min-h-0 !max-h-[50vh] overflow-y-auto overscroll-contain rounded-lg bg-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
|
||||||
{availableVoices.map((voiceId) => (
|
{availableVoices.map((voiceId) => (
|
||||||
<ListboxOption
|
<ListboxOption
|
||||||
key={voiceId}
|
key={voiceId}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue