import React, { HTMLAttributes, RefCallback } from 'react'; import { getClassName } from '../../helpers/getClassName'; import { classNames } from '../../lib/classNames'; import { transitionEndEventName, TransitionStartEventDetail, transitionStartEventName } from '../View/View'; import { withContext } from '../../hoc/withContext'; import { HasPlatform, HasRootRef } from '../../types'; import { withPlatform } from '../../hoc/withPlatform'; import { withPanelContext } from '../Panel/withPanelContext'; import { setRef } from '../../lib/utils'; import { SplitColContext, SplitColContextProps } from '../SplitCol/SplitCol'; import { DOMProps, withDOM } from '../../lib/dom'; export interface FixedLayoutProps extends HTMLAttributes, HasRootRef, HasPlatform { vertical?: 'top' | 'bottom'; /** * Это свойство определяет, будет ли фон компонента окрашен в цвет фона контента. * Это часто необходимо для фиксированных кнопок в нижней части экрана. */ filled?: boolean; /** * @ignore */ panel?: string; /** * @ignore */ splitCol?: SplitColContextProps; } export interface FixedLayoutState { position: 'absolute' | null; top: number; width: string; } class FixedLayout extends React.Component { state: FixedLayoutState = { position: 'absolute', top: null, width: '', }; el: HTMLDivElement; private onMountResizeTimeout: number; get document() { return this.props.document; } get window() { return this.props.window; } get currentPanel(): HTMLElement { const { panel: id } = this.props; const elem = this.document.getElementById(id); if (!elem) { console.warn(`Element #${id} not found`); } return elem; } get canTargetPanelScroll() { const panelEl = this.currentPanel; if (!panelEl) { return true; // Всегда предпологаем, что может быть скролл в случае, если нет document } return panelEl.scrollHeight > panelEl.clientHeight; } componentDidMount() { this.onMountResizeTimeout = setTimeout(() => this.doResize()); this.window.addEventListener('resize', this.doResize); this.document.addEventListener(transitionStartEventName, this.onViewTransitionStart); this.document.addEventListener(transitionEndEventName, this.onViewTransitionEnd); } componentWillUnmount() { clearInterval(this.onMountResizeTimeout); this.window.removeEventListener('resize', this.doResize); this.document.removeEventListener(transitionStartEventName, this.onViewTransitionStart); this.document.removeEventListener(transitionEndEventName, this.onViewTransitionEnd); } onViewTransitionStart: EventListener = (e: CustomEvent) => { let panelScroll = e.detail.scrolls[this.props.panel] || 0; const fromPanelHasScroll = this.props.panel === e.detail.from && panelScroll > 0; const toPanelHasScroll = this.props.panel === e.detail.to && panelScroll > 0; // Для панелей, с которых уходим всегда выставляется скролл // Для панелей на которые приходим надо смотреть, есть ли браузерный скролл if (fromPanelHasScroll || toPanelHasScroll && this.canTargetPanelScroll) { this.setState({ position: 'absolute', top: this.el.offsetTop + panelScroll, width: '', }); } }; onViewTransitionEnd: VoidFunction = () => { this.setState({ position: null, top: null, }); this.doResize(); }; doResize = () => { const { colRef } = this.props.splitCol; if (colRef && colRef.current) { const node: HTMLElement = colRef.current; const width = node.offsetWidth; this.setState({ width: `${width}px`, position: null }); } else { this.setState({ width: '', position: null }); } }; getRef: RefCallback = (element) => { this.el = element; setRef(element, this.props.getRootRef); }; render() { const { className, children, style, vertical, getRootRef, platform, filled, splitCol, panel, window, document, ...restProps } = this.props; return (
{children}
); } } export default withContext( withPlatform(withPanelContext(withDOM(FixedLayout))), SplitColContext, 'splitCol', );