/** * useConfigPanelPosition - ConfigPanelのポジショニング専用カスタムフック * * このフックは、Floating UIを使用したポジショニングロジックを集約します。 * - 保存座標での仮想参照要素の作成。基準要素(anchorRef)を渡すと、開いた後に基準要素が動いた分だけ * 座標をずらし、ページではなく内側の要素がスクロールする画面でも、パネルがフィルターと一緒に動く * - スクロールしたら位置を計算し直す。基準要素がない場合は、位置を最初に求めた値に固定する * - パネルがスクロール領域の見えている範囲より上に出たら、その分を切る(固定ヘッダーの下に潜らせる) * - 使える高さは、参照要素ごとに最初に求めた値に固定(スクロール位置で Content の最大高さを変えない。 * 保存座標が変わって参照要素を作り直したときは、その時点の値で取り直す) * - はみ出し防止(shift, size middleware) * - Content領域の最大高さの動的計算(パネル自身の高さが変わったときに計算し直す) * - ContextMenu2と同じ方式の領域外クリック処理 * * @example * ```tsx * const { * refs, * floatingStyles, * getFloatingProps, * isPositioned, * contentMaxHeight, * } = useConfigPanelPosition({ isOpen, savedCoordinates, onCancel, contentRef }); * ``` */ import { FloatingContext, type Coords, type Middleware, type ReferenceElement, type VirtualElement } from "@floating-ui/react"; export type MenuCoordinates = { x: number; y: number; }; export type UseConfigPanelPositionProps = { isOpen: boolean; savedCoordinates?: MenuCoordinates | null; onCancel: () => void; /** スクロールする Content 要素。渡すと、Content 以外の高さを実測して最大高さを決める */ contentRef?: React.RefObject; /** 位置の基準にする要素。保存座標を求めたトリガーと一緒に動く要素を渡す */ anchorRef?: React.RefObject; }; /** * Content(スクロール領域)の最大高さを求める。 * ヘッダー・説明文・エラー・ボタンなど Content 以外の高さは、描画済みならパネルの実寸から求める。 * 未描画(高さ 0)のときは概算の定数で代用する。 */ export declare const calculateContentMaxHeight: ({ availableHeight, floatingHeight, contentHeight, }: { availableHeight: number; floatingHeight: number; contentHeight: number | null; }) => number; /** * 保存座標から仮想参照要素を作る。 * 保存座標は開いた時点のビューポート座標なので、それだけでは、ページではなく内側の要素がスクロールしたときに * パネルが画面上の同じ位置に残る(パネルの配置の基準(offsetParent)がその要素の外にあると、 * その要素のスクロールではパネルが動かないため)。 * 基準要素を渡すと、開いた後に基準要素が動いた分だけ座標をずらす。 * 基準要素は contextElement にも渡し、autoUpdate がその要素を含むスクロール領域の scroll を拾えるようにする */ export declare const createAnchoredReference: (coordinates: MenuCoordinates, anchor: Element | null) => VirtualElement; /** 参照要素と、その参照要素で求めた値の組 */ export type ReferenceValue = { reference: ReferenceElement; value: T; }; /** * パネルの位置に、最新の参照要素で求めた位置を使う middleware。 * 基準要素のない参照要素は、開いた時点のビューポート座標のままなので、スクロールした後に位置を計算し直すと、 * パネルが開いた時点の画面位置へ戻ってしまう。その場合は、最初に求めた位置に固定する。 * 横のはみ出しの補正は後続の shift で、Content の最大高さは後続の size で求め直す */ export declare const keepLatestReferencePosition: (positionRef: { current: ReferenceValue | null; }, latestReferenceRef: { current: ReferenceElement | null; }) => Middleware; /** 参照要素と、その参照要素で値を求めたときの参照点と値の組 */ export type ReferencePointValue = ReferenceValue & { point: Coords; }; /** * shift で横にずらした量を、スクロールのあいだは保つ middleware(shift の直後に置く)。 * 基準要素に追従してスクロールのたびに計算し直すと、shift も計算し直される。すると、フィルターが横にスクロールして * 画面の外へ流れても、パネルだけが画面の端(8px)で止まり、フィルターから離れる。スクロールでは前のずらし量を保ち、 * フィルターと一緒に動かす。パネルの大きさが変わったとき(説明文の表示やバリデーションエラー)は計算し直し、 * 画面の中に収め直す */ export declare const keepShiftWhileScrolling: (shiftRef: { current: ReferencePointValue | null; }, latestReferenceRef: { current: ReferenceElement | null; }) => Middleware; /** 基準要素を含むスクロール領域の、見えている範囲の上端(ビューポート座標) */ export declare const getVisibleTop: (anchor: Element) => number; /** * パネルの上端が、スクロール領域の見えている範囲より上に出た分(px)を middlewareData に返す。 * パネルはフィルターに追従するので、フィルターが固定ヘッダーの下に隠れるまでスクロールすると、 * パネルの上側だけがヘッダーに重なって見える。本文と同じようにヘッダーの下に潜らせるため、はみ出した分を切る。 * 上側だけを切るのは、モーダルなど高さの低いスクロール領域の下の方で開いたときに、 * これまでどおり領域の外まで表示するため。 * パネルの上端は参照点と同じ高さに置いている(bottom-start、縦にはずらさない)ので、参照点から求める。 * 固定ヘッダーがスクロール領域の外にある作りを前提にしている(スクロール領域の中の sticky のヘッダーは見ていない) */ export declare const measureClipAboveScrollArea: (clipTopRef: { current: ReferenceValue | null; }, latestReferenceRef: { current: ReferenceElement | null; }) => Middleware; export type UseConfigPanelPositionReturn = { refs: { setReference: (node: any) => void; setFloating: (node: HTMLElement | null) => void; floating: React.MutableRefObject; }; floatingStyles: React.CSSProperties; getFloatingProps: (userProps?: React.HTMLProps) => Record; isPositioned: boolean; contentMaxHeight: number; context: FloatingContext; }; export declare const useConfigPanelPosition: ({ isOpen, savedCoordinates, onCancel, contentRef, anchorRef, }: UseConfigPanelPositionProps) => UseConfigPanelPositionReturn;