import { LAYOUT_TYPE, withLayoutType } from '@cloud-ru/ds-adaptive'; import { APPEARANCE, Button, BUTTON_GROUP_ACTION_SLOT, ButtonGroup, ICON_POSITION, VIEW } from '@cloud-ru/ds-button'; import { Dropdown } from '@cloud-ru/ds-dropdown'; import { ChevronRightSVG, KebabSVG } from '@cloud-ru/ds-icons/interface/system'; import { Droplist } from '@cloud-ru/ds-list'; import { ModalCustom } from '@cloud-ru/ds-modal'; import { WithTooltip } from '@cloud-ru/ds-tooltip'; import { SIZE, Typography, VARIANT } from '@cloud-ru/ds-typography'; import { extractSupportProps } from '@cloud-ru/ds-utils'; import cn from 'classnames'; import { useState } from 'react'; import { TEST_IDS } from '../../constants'; import { Headline } from '../Headline'; import { useGetButtonLabel } from './hooks'; import styles from './mobileStyles.module.scss'; import { DesktopPageFormProps } from './types'; export type MobilePageFormProps = DesktopPageFormProps; /** Убирает PageForm-специфичные поля (`variant`/`tooltip`/`size`), оставляя пропсы `@cloud-ru/ds-button` для `ButtonGroup`. */ function stripFooterButton(button: T): Omit { const buttonProps = { ...button } as Record; delete buttonProps.variant; delete buttonProps.tooltip; delete buttonProps.size; return buttonProps as Omit; } function MobilePageFormBase({ children, title, subtitle, className, footer, stepper, priceSummary, sideBlock, ...rest }: MobilePageFormProps) { const [openPriceSummary, setOpenPriceSummary] = useState(false); const [openMore, setOpenMore] = useState(false); const [openMoreContentIndex, setOpenMoreContentIndex] = useState(undefined); const getButtonLabel = useGetButtonLabel(); const footerTooltips = footer && { [BUTTON_GROUP_ACTION_SLOT.Primary]: footer.buttonPrimary.tooltip, [BUTTON_GROUP_ACTION_SLOT.Secondary]: footer.buttonSecondary?.tooltip, [BUTTON_GROUP_ACTION_SLOT.Tertiary]: footer.buttonAdditional?.tooltip, }; return (
({ id: idx, content: { label: label, }, onClick: () => { setOpenMoreContentIndex(idx); setOpenMore(false); }, }))} >
{sideBlock && ( setOpenMoreContentIndex(undefined)} // Высоту листа задаёт сам sideBlock-элемент (`snapPoints`); напр. `[1]` — на всю высоту. // По умолчанию — по высоте контента. snapPoints={sideBlock[openMoreContentIndex ?? 0].snapPoints} > )}
{children}
{(priceSummary || footer) && (
{priceSummary && (
Оценка бюджета {priceSummary.content}
} data-test-id={TEST_IDS.pageForm.priceSummarySheet} > {typeof priceSummary.total === 'string' ? (
)} {footer && ( = 3 } renderAction={(button, slot) => { const tooltip = footerTooltips?.[slot]; // disableSpanWrapper — чтобы кнопка осталась прямым flex-потомком группы (раскладка `filled`). return ( {button} ); }} primaryAction={{ ...stripFooterButton(footer.buttonPrimary), view: VIEW.Filled, appearance: APPEARANCE.Primary, label: footer.buttonPrimary.variant === 'custom' ? footer.buttonPrimary.label : getButtonLabel(footer.buttonPrimary.variant), }} secondaryAction={ footer.buttonSecondary ? { ...stripFooterButton(footer.buttonSecondary), view: VIEW.Outline, appearance: APPEARANCE.Neutral, label: footer.buttonSecondary.variant === 'custom' ? footer.buttonSecondary.label : getButtonLabel(footer.buttonSecondary.variant), } : undefined } tertiaryAction={ footer.buttonAdditional ? { ...stripFooterButton(footer.buttonAdditional), view: VIEW.Simple, appearance: APPEARANCE.Neutral, } : undefined } /> )}
)} ); } /** * Мобильный `PageForm`. Форсирует `mobile`-раскладку через `withLayoutType`, чтобы вложенные * адаптивные `@ds`-примитивы (`Dropdown` price-summary, `ModalCustom` sideBlock) рендерили * bottom-sheet даже под desktop-провайдером. */ export const MobilePageForm = withLayoutType(MobilePageFormBase, LAYOUT_TYPE.Mobile);