@@ -1,5 +1,5 @@ -import React from 'react' -import { Tooltip } from '@mui/material' +import React, { useState } from 'react' +import { ClickAwayListener, Tooltip, useMediaQuery } from '@mui/material' interface Props { title?: string @@ -21,13 +21,39 @@ interface Props { | 'top-end' | 'top-start' | 'top' + /** Растянуть триггер на 100% ширины родителя (flex/grid + disabled-кнопка). */ + fullWidthTrigger?: boolean + /** На устройствах без hover (тач) открывать/закрывать по нажатию, снаружи — закрытие. */ + tapToOpenOnMobile?: boolean } /** Выше оверлеев модалок (см. template.module.scss — 9999) */ const TOOLTIP_POPPER_Z_INDEX = 10050 -export const TooltipCustom: React.FC = ({ children, title, placement, className, maxWidth, stopClickPropagation = true }) => { - return ( +export const TooltipCustom: React.FC = ({ + children, + title, + placement, + className, + maxWidth, + stopClickPropagation = true, + fullWidthTrigger = false, + tapToOpenOnMobile = false, +}) => { + const isTouchPrimary = useMediaQuery('(hover: none)', { noSsr: true }) + const tapMode = Boolean(tapToOpenOnMobile && isTouchPrimary) + const [tapOpen, setTapOpen] = useState(false) + + const triggerSpanStyle: React.CSSProperties | undefined = fullWidthTrigger + ? { display: 'flex', width: '100%', minWidth: 0 } + : undefined + + const tooltip = ( setTapOpen(false) : undefined} + disableHoverListener={tapMode} + disableFocusListener={tapMode} + disableTouchListener={tapMode} onClick={stopClickPropagation ? (e: React.MouseEvent) => e.stopPropagation() : undefined} PopperProps={{ sx: { zIndex: TOOLTIP_POPPER_Z_INDEX }, @@ -60,7 +86,21 @@ export const TooltipCustom: React.FC = ({ children, title, placement, cla arrow placement={placement} > - {children} + { + if (stopClickPropagation) e.stopPropagation() + setTapOpen((v) => !v) + } + : undefined + } + > + {children} + ) + + return tapMode ? setTapOpen(false)}>{tooltip} : tooltip } @@ -23,10 +23,6 @@ margin-left: auto; margin-right: calc(3%); margin-top: 0; - - span { - width: 100%; - } } .header { @@ -28,7 +28,7 @@ import { useShowDataStore } from '#/shared/lib/hooks/use-show-data' import { SvgIcon } from '#/shared/ui/svg' import ProgressLoader from '#/widgets/loaders/progress-loader-props' import { AudioMessagesList } from '#/widgets/messages/ui/audio-messages-list' -import { ModelApiView, StaticTabs } from '#/widgets/model-api-view' +import { ModelApiView, StaticTabsVoiceCloning } from '#/widgets/model-api-view' import { useDeviceType } from '#/shared/lib/hooks' import { AddVoiceCta } from '../add-voice-cta' @@ -220,6 +220,7 @@ const VoiceCloningModelPage: NextPageWithLayout = () => { enabled: !!modelType && !!session?.access && scope === 'playground', }) const hasGenerations = (messages?.length ?? 0) > 0 + const generationsInitialLoading = loading && offset.current === 0 const mobileScrollAreaHeight = `calc(100dvh - 116px - 61px - 15px ${botParams?.blocked ? '- 17px' : '- 110px'})` const compactPlaygroundEmpty = scope === 'playground' && !hasGenerations /** Меню + отступы layout + шапка страницы + табы — чтобы основной блок доходил до низа экрана без серой полосы */ @@ -248,21 +249,17 @@ const VoiceCloningModelPage: NextPageWithLayout = () => { {desktop && ( - - + - )} {!desktop && ( - - + - )} @@ -336,6 +333,23 @@ const VoiceCloningModelPage: NextPageWithLayout = () => { position: 'relative', }} > +
+
+ +
+
{ : {}), }} > -
+
> +} + +export const StaticTabsVoiceCloning = memo(({ scope, setScope }: IProps) => { + const tabs: { scope: 'playground' | 'api'; title: string }[] = [ + { scope: 'playground', title: 'Playground' }, + { scope: 'api', title: 'API' }, + ] + + return ( + + {tabs.map((el) => { + const isApiDisabled = el.scope === 'api' + const toggleButton = ( + setScope(el.scope)} + className={styles.item} + value={el.scope} + selected={scope === el.scope} + disabled={isApiDisabled} + sx={isApiDisabled ? { pointerEvents: 'none' } : undefined} + > + + + {el.title} + + + + ) + + return ( + + {isApiDisabled ? ( + + {toggleButton} + + ) : ( + toggleButton + )} + + ) + })} + + ) +}) @@ -1,3 +1,19 @@ +.tabCell { + flex: 1; + min-width: 0; + display: flex; + align-items: stretch; +} + +.tabTooltipTarget { + display: flex; + flex: 1; + min-width: 0; + width: 100%; + align-items: center; + justify-content: center; +} + .container { display: flex; border: none; @@ -5,11 +5,10 @@ import styles from './static-tabs.module.scss' interface IProps { scope: 'playground' | 'api' - disabledApi?: boolean setScope: React.Dispatch> } -export const StaticTabs = memo(({ scope, setScope, disabledApi = false }: IProps) => { +export const StaticTabs = memo(({ scope, setScope }: IProps) => { const tabs: { scope: 'playground' | 'api'; title: string }[] = [ { scope: 'playground', title: 'Playground' }, { scope: 'api', title: 'API' }, @@ -27,7 +26,6 @@ export const StaticTabs = memo(({ scope, setScope, disabledApi = false }: IProps setScope(el.scope)} - disabled={disabledApi} key={el.scope} className={styles.item} value={el.scope}