diff --git a/src/components/CollapseButton.tsx b/src/components/CollapseButton.tsx index 80f8c41..ddb166e 100644 --- a/src/components/CollapseButton.tsx +++ b/src/components/CollapseButton.tsx @@ -5,7 +5,7 @@ const Button = styled.div` width: 1.8rem; height: 1.8rem; border-radius: 300px; - background: #0092d1; + background: #0e6ff9; cursor: pointer; user-select: none; text-align: center; diff --git a/src/components/Pane/index.tsx b/src/components/Pane/index.tsx index 499334a..8f6726d 100644 --- a/src/components/Pane/index.tsx +++ b/src/components/Pane/index.tsx @@ -3,6 +3,7 @@ import { useMergeClasses } from '../../hooks/useMergeClasses'; import { useEffect, useMemo, useRef, useState } from 'react'; import styled, { css } from 'styled-components'; import { SplitType } from '../SplitPane'; +import type { DragState } from '../SplitPane/hooks/effects/useDragState'; const DEFAULT_COLLAPSE_TRANSITION_TIMEOUT = 500; const verticalCss = css` @@ -26,6 +27,7 @@ interface PaneRootProps { $shouldAnimate: boolean; $timeout: number; } + const PaneRoot = styled.div` position: relative; outline: none; @@ -63,6 +65,7 @@ const CollapseOverlay = styled.div<{ $timeout: number; $isCollapsed: boolean }>` export interface PaneProps { size: number; minSize: number; + maxSize: number | undefined; isVertical: boolean; split: SplitType; className?: string; @@ -72,10 +75,14 @@ export interface PaneProps { collapsedIndices: number[]; children: React.ReactNode; transitionTimeout: number | undefined; + flexGrow: number | undefined; + flexShrink: number | undefined; + dragState: DragState | null; } const UnMemoizedPane = ({ size, minSize, + maxSize, isCollapsed, collapseOverlayCss = { background: 'rgba(220,220,220, 0.1)' }, isVertical, @@ -85,6 +92,8 @@ const UnMemoizedPane = ({ forwardRef, collapsedIndices, transitionTimeout, + flexGrow, + flexShrink, }: PaneProps) => { const classes = useMergeClasses(['Pane', split, className]); const timeout = useMemo(() => transitionTimeout ?? DEFAULT_COLLAPSE_TRANSITION_TIMEOUT, [ @@ -109,9 +118,21 @@ const UnMemoizedPane = ({ minSize, isVertical, ]); + const maxStyle = maxSize ? useMemo(() => (isVertical ? { maxWidth: maxSize } : { maxHeight: maxSize }), [ + maxSize, + isVertical, + ]) : {}; const widthPreserverStyle: React.CSSProperties = isCollapsed - ? { ...minStyle, userSelect: 'none' } - : minStyle; + ? { ...minStyle, ...maxStyle, userSelect: 'none' } + : { ...minStyle, ...maxStyle }; + + const PaneRootMinMaxStyle: React.CSSProperties = isVertical + ? isCollapsed + ? { minWidth: 0 } + : { minWidth: minSize, maxWidth: maxSize } + : isCollapsed + ? { minHeight: 0 } + : { minHeight: minSize, maxHeight: maxSize } return ( diff --git a/src/components/Resizer/index.tsx b/src/components/Resizer/index.tsx index 6379576..a588fa4 100644 --- a/src/components/Resizer/index.tsx +++ b/src/components/Resizer/index.tsx @@ -13,6 +13,7 @@ import { CollapseOptions, ResizerOptions } from '../SplitPane'; import { useTransition } from './hooks/useTransition'; import { SplitType } from '../SplitPane'; import { debounce } from '../SplitPane/helpers'; +import type { DragState } from '../SplitPane/hooks/effects/useDragState'; const defaultResizerOptions: Required = { grabberSize: '1rem', @@ -31,6 +32,7 @@ export interface ResizerProps { onDragStarted: BeginDragCallback; onCollapseToggle: (paneIndex: number) => void; isCollapsed: boolean; + dragState: DragState | null; } export const Resizer = ({ isVertical, @@ -43,8 +45,15 @@ export const Resizer = ({ onCollapseToggle, isLtr, isCollapsed, + dragState, }: ResizerProps) => { - const { grabberSize, css, hoverCss } = { ...defaultResizerOptions, ...resizerOptions }; + + let { grabberSize, css, hoverCss } = { ...defaultResizerOptions, ...resizerOptions }; + + // to avoid iframe take over the mouse event + if (dragState !== null) { + grabberSize = '100rem'; + } const classes = useMergeClasses(['Resizer', split, className]); const grabberSizeWithUnit = useMemo(() => getSizeWithUnit(grabberSize), [grabberSize]); diff --git a/src/components/SplitPane/helpers.tsx b/src/components/SplitPane/helpers.tsx index 2a1fd44..74a81ff 100644 --- a/src/components/SplitPane/helpers.tsx +++ b/src/components/SplitPane/helpers.tsx @@ -4,6 +4,8 @@ import { SplitType } from '.'; import { Nullable } from '../../types/utilities'; export const DEFAULT_MIN_SIZE = 50; +export const DEFAULT_FLEX_GROW = 1; +export const DEFAULT_FLEX_SHRINK = 1; export const getMinSize = (index: number, minSizes?: number | number[]): number => { if (typeof minSizes === 'number') { @@ -19,6 +21,48 @@ export const getMinSize = (index: number, minSizes?: number | number[]): number return DEFAULT_MIN_SIZE; }; +export const getMaxSize = (index: number, maxSizes?: number | number[]): number | undefined => { + if (typeof maxSizes === 'number') { + if (maxSizes > 0) { + return maxSizes; + } + } else if (maxSizes instanceof Array) { + const value = maxSizes[index]; + if (value > 0) { + return value; + } + } + return undefined; +}; + +export const getFlexGrow = (index: number, flexGrow?: number | number[]): number => { + if (typeof flexGrow === 'number') { + if (flexGrow >= 0) { + return flexGrow; + } + } else if (flexGrow instanceof Array) { + const value = flexGrow[index]; + if (value >= 0) { + return value; + } + } + return DEFAULT_FLEX_GROW; +}; + +export const getFlexShrink = (index: number, flexShrink?: number | number[]): number => { + if (typeof flexShrink === 'number') { + if (flexShrink >= 0) { + return flexShrink; + } + } else if (flexShrink instanceof Array) { + const value = flexShrink[index]; + if (value >= 0) { + return value; + } + } + return DEFAULT_FLEX_SHRINK; +}; + export const getRefSize = ({ ref, split, diff --git a/src/components/SplitPane/index.tsx b/src/components/SplitPane/index.tsx index b3d09dd..e24aa2f 100644 --- a/src/components/SplitPane/index.tsx +++ b/src/components/SplitPane/index.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react'; import { Pane } from '../Pane'; import { Resizer } from '../Resizer'; import { useSplitPaneResize } from './hooks/useSplitPaneResize'; -import { convertCollapseSizesToIndices, getMinSize, Wrapper } from './helpers'; +import { convertCollapseSizesToIndices, getMinSize, Wrapper, getFlexGrow, getFlexShrink, getMaxSize } from './helpers'; import { useMergeClasses } from '../../hooks/useMergeClasses'; import { useIsCollapseReversed } from './hooks/memos/useIsCollapseReversed'; import { useToggleCollapse } from './hooks/callbacks/useToggleCollapse'; @@ -50,12 +50,15 @@ export interface SplitPaneProps { initialSizes?: number[]; minSizes?: number | number[]; + maxSizes?: number | number[]; collapsedSizes?: Nullable[]; hooks?: SplitPaneHooks; resizerOptions?: ResizerOptions; children: React.ReactChild[]; + flexGrows: number | number[]; + flexShrinks: number | number[]; } export const SplitPane: React.FC = (props) => { @@ -99,17 +102,6 @@ export const SplitPane: React.FC = (props) => { return <>{props.children}; } - // STATE: if dragging, contains which pane is dragging and what the offset is. If not dragging then null - // const { dragState } = useDragState(isVertical, () => {}); - - const mouseEventBlockerStyle: React.CSSProperties = { - width: '100%', - height: '100%', - position: 'fixed', - zIndex: 100, - opacity: 0, - }; - // stacks the children and places a resizer in between each of them. Each resizer has the same index as the pane that it controls. const entries = childPanes.map((pane, paneIndex) => { const resizerPaneIndex = isReversed ? paneIndex : paneIndex - 1; @@ -128,9 +120,9 @@ export const SplitPane: React.FC = (props) => { collapseOptions={collapseOptions} onDragStarted={handleDragStart} onCollapseToggle={toggleCollapse} + dragState={dragState} /> ) : null} - {dragState !== null &&
} = (props) => { split={props.split} isVertical={isVertical} minSize={getMinSize(paneIndex, props.minSizes)} + maxSize={getMaxSize(paneIndex, props.maxSizes)} className={props.className} transitionTimeout={collapseOptions?.collapseTransitionTimeout} collapseOverlayCss={collapseOptions?.overlayCss} + flexGrow={getFlexGrow(paneIndex, props.flexGrows)} + flexShrink={getFlexShrink(paneIndex, props.flexShrinks)} + dragState={dragState} > {pane.node} diff --git a/yarn.lock b/yarn.lock index a72d894..e89b4b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2700,9 +2700,9 @@ "@types/react" "*" "@types/react-dom@^17.0.4": - version "17.0.10" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.10.tgz#d6972ec018d23cf22b99597f1289343d99ea9d9d" - integrity sha512-8oz3NAUId2z/zQdFI09IMhQPNgIbiP8Lslhv39DIDamr846/0spjZK0vnrMak0iB8EKb9QFTTIdg2Wj2zH5a3g== + version "17.0.11" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.11.tgz#e1eadc3c5e86bdb5f7684e00274ae228e7bcc466" + integrity sha512-f96K3k+24RaLGVu/Y2Ng3e1EbZ8/cVJvypZWd7cy0ofCBaf2lcM46xNhycMZ2xGwbBjRql7hOlZ+e2WlJ5MH3Q== dependencies: "@types/react" "*" @@ -2721,9 +2721,9 @@ "@types/react" "*" "@types/react@*", "@types/react@^17.0.5": - version "17.0.33" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.33.tgz#e01ae3de7613dac1094569880bb3792732203ad5" - integrity sha512-pLWntxXpDPaU+RTAuSGWGSEL2FRTNyRQOjSWDke/rxRg14ncsZvx8AKWMWZqvc1UOaJIAoObdZhAWvRaHFi5rw== + version "17.0.34" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.34.tgz#797b66d359b692e3f19991b6b07e4b0c706c0102" + integrity sha512-46FEGrMjc2+8XhHXILr+3+/sTe3OfzSPU9YGKILLrUYbQ1CLQC9Daqo1KzENGXAWwrFwiY0l4ZbF20gRvgpWTg== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -3359,9 +3359,9 @@ asn1.js@^5.2.0: safer-buffer "^2.1.0" asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + version "0.2.6" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" + integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== dependencies: safer-buffer "~2.1.0" @@ -4110,9 +4110,9 @@ camelize@^1.0.0: integrity sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs= caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001125, caniuse-lite@^1.0.30001274: - version "1.0.30001274" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001274.tgz#26ca36204d15b17601ba6fc35dbdad950a647cc7" - integrity sha512-+Nkvv0fHyhISkiMIjnyjmf5YJcQ1IQHZN6U9TLUMroWR38FNwpsC51Gb68yueafX1V6ifOisInSgP9WJFS13ew== + version "1.0.30001278" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001278.tgz#51cafc858df77d966b17f59b5839250b24417fff" + integrity sha512-mpF9KeH8u5cMoEmIic/cr7PNS+F5LWBk0t2ekGT60lFf0Wq+n9LspAj0g3P+o7DQhD3sUdlMln4YFAWhFYn9jg== capture-exit@^2.0.0: version "2.0.0" @@ -5151,9 +5151,9 @@ ee-first@1.1.1: integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= electron-to-chromium@^1.3.564, electron-to-chromium@^1.3.886: - version "1.3.887" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.887.tgz#b36aeed12a28aaa19460a467823f5bbe1f3c6f06" - integrity sha512-QQUumrEjFDKSVYVdaeBmFdyQGoaV+fCSMyWHvfx/u22bRHSTeBQYt6P4jMY+gFd4kgKB9nqk7RMtWkDB49OYPA== + version "1.3.889" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.889.tgz#0b7c6f7628559592d5406deda281788f37107790" + integrity sha512-suEUoPTD1mExjL9TdmH7cvEiWJVM2oEiAi+Y1p0QKxI2HcRlT44qDTP2c1aZmVwRemIPYOpxmV7CxQCOWcm4XQ== element-resize-detector@^1.2.2: version "1.2.3" @@ -5844,9 +5844,9 @@ extsprintf@1.3.0: integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + version "1.4.1" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" + integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" @@ -6891,9 +6891,9 @@ ignore@^4.0.3, ignore@^4.0.6: integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== ignore@^5.1.4: - version "5.1.8" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" - integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== + version "5.1.9" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.9.tgz#9ec1a5cbe8e1446ec60d4420060d43aa6e7382fb" + integrity sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ== immer@8.0.1: version "8.0.1" @@ -8301,9 +8301,9 @@ loader-utils@^1.2.3, loader-utils@^1.4.0: json5 "^1.0.1" loader-utils@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.1.tgz#3b8d4386f42378d6434d32d7bc08e7a52d39575e" - integrity sha512-g4miPa9uUrZz4iElkaVJgDFwKJGh8aQGM7pUL4ejXl6cu7kSb30seQOVGNMP6sW8j7DW77X68hJZ+GM7UGhXeQ== + version "2.0.2" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.2.tgz#d6e3b4fb81870721ae4e0868ab11dd638368c129" + integrity sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A== dependencies: big.js "^5.2.2" emojis-list "^3.0.0"