Skip to content

Commit

Permalink
Merge pull request #2712 from XiaoMi/feature/resize-box
Browse files Browse the repository at this point in the history
feat(resize-box): 增加收缩功能
  • Loading branch information
solarjoker authored Jan 17, 2024
2 parents d247866 + 7b3c5fd commit a8444cc
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 14 deletions.
2 changes: 1 addition & 1 deletion packages/ui/resize-box/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@hi-ui/resize-box",
"version": "4.0.2",
"version": "4.0.3-alpha.1",
"description": "A sub-package for @hi-ui/hiui.",
"keywords": [],
"author": "HiUI <[email protected]>",
Expand Down
57 changes: 54 additions & 3 deletions packages/ui/resize-box/src/ResizeBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { __DEV__ } from '@hi-ui/env'
import { HiBaseHTMLProps } from '@hi-ui/core'
import { useMergeRefs } from '@hi-ui/use-merge-refs'
import { useUncontrolledState } from '@hi-ui/use-uncontrolled-state'
import { LeftOutlined } from '@hi-ui/icons'
import { ResizeBoxPane, ResizeBoxPaneProps } from './ResizeBoxPane'
import { Separator, SeparatorProps } from './Separator'

Expand All @@ -29,6 +30,7 @@ export const ResizeBox = forwardRef<HTMLDivElement | null, ResizeBoxProps>(

const [colWidths, tryChangeColWidths] = useUncontrolledState<number[]>([])
const minColWidthsRef = React.useRef<number[]>([])
const oldColWidthsRef = React.useRef<number[]>([])

const draggableRef = React.useRef<boolean>(true)

Expand Down Expand Up @@ -103,19 +105,63 @@ export const ResizeBox = forwardRef<HTMLDivElement | null, ResizeBoxProps>(
}

const { type, props } = child
const { style, onResizeStart, onResizeEnd, onResize, ...rest } = props
const {
collapsible,
style,
onResizeStart,
onResizeEnd,
onResize,
onCollapse,
collapsed: collapsedProp,
...rest
} = props

if (type !== ResizeBoxPane) {
console.error('ResizeBox children must be ResizeBoxPane')
return
}

if (index !== children?.length - 1) {
const collapsed = collapsedProp ?? colWidths[index] === 0

return (
<Resizable
className={`${prefixCls}__resizable`}
draggableOpts={{ enableUserSelectHack: false }}
handle={<Separator {...separatorProps} />}
handle={
<div style={{ position: 'relative' }}>
<Separator {...separatorProps} />
{collapsible && (
<div
className={cx(`${prefixCls}-toggle`)}
onClick={() => {
// 可折叠受控
if (collapsedProp !== undefined) {
const currentPaneWidth = colWidths[index]
if (currentPaneWidth !== 0) {
oldColWidthsRef.current[index] = currentPaneWidth
}
onCollapse?.(!collapsed)
} else {
tryChangeColWidths((prev) => {
const nextColWidths = [...prev]
const currentPaneWidth = nextColWidths[index]
if (currentPaneWidth !== 0) {
nextColWidths[index] = 0
oldColWidthsRef.current[index] = currentPaneWidth
} else {
nextColWidths[index] = oldColWidthsRef.current[index]
}
return nextColWidths
})
}
}}
>
<LeftOutlined />
</div>
)}
</div>
}
height={0}
width={colWidths[index] ?? 0}
onResizeStart={() => {
Expand Down Expand Up @@ -170,9 +216,14 @@ export const ResizeBox = forwardRef<HTMLDivElement | null, ResizeBoxProps>(
>
{React.cloneElement(child, {
...rest,
collapsed,
style: {
...style,
width: colWidths[index],
width: collapsedProp
? collapsed
? 0
: oldColWidthsRef.current[index]
: colWidths[index],
},
})}
</Resizable>
Expand Down
8 changes: 7 additions & 1 deletion packages/ui/resize-box/src/ResizeBoxPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,17 @@ export const ResizeBoxPane = forwardRef<HTMLDivElement | null, ResizeBoxPaneProp
children,
defaultWidth,
minWidth,
collapsible,
collapsed,
onCollapse,
onResizeStart,
onResizeEnd,
onResize,
...rest
},
ref
) => {
const cls = cx(prefixCls, className)
const cls = cx(prefixCls, className, collapsed && `${prefixCls}--collapsed`)

return (
<div ref={ref} role={role} className={cls} style={{ width: defaultWidth }} {...rest}>
Expand All @@ -35,6 +38,9 @@ export interface ResizeBoxPaneProps extends HiBaseHTMLProps<'div'> {
defaultWidth?: number
width?: number
minWidth?: number
collapsible?: boolean
collapsed?: boolean
onCollapse?: (collapsed: boolean) => void
onResizeStart?: () => void
onResizeEnd?: () => void
onResize?: (width: number) => void
Expand Down
41 changes: 37 additions & 4 deletions packages/ui/resize-box/src/Separator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,58 @@ import React, { forwardRef } from 'react'
import { cx, getPrefixCls } from '@hi-ui/classname'
import { __DEV__ } from '@hi-ui/env'
import { HiBaseHTMLProps } from '@hi-ui/core'
import { LeftOutlined } from '@hi-ui/icons'

const SEPARATOR_PREFIX = getPrefixCls('resize-box-separator')

export const Separator = forwardRef<HTMLDivElement | null, SeparatorProps>(
(
{ prefixCls = SEPARATOR_PREFIX, role = 'resize-box-separator', className, children, ...rest },
{
prefixCls = SEPARATOR_PREFIX,
role = 'resize-box-separator',
className,
children,
collapsible,
collapsed,
onToggle,
...rest
},
ref
) => {
const cls = cx(prefixCls, className)
const cls = cx(prefixCls, className, collapsed && `${prefixCls}--collapsed`)

return (
<div ref={ref} role={role} className={cls} {...rest}>
<span className={prefixCls + '-content'} />
<span className={`${prefixCls}-content`} />
{collapsible && (
<div
className={cx(`${prefixCls}-toggle`)}
onClick={() => {
onToggle?.()
}}
>
<LeftOutlined />
</div>
)}
</div>
)
}
)

export interface SeparatorProps extends HiBaseHTMLProps<'div'> {}
export interface SeparatorProps extends HiBaseHTMLProps<'div'> {
/**
* 可折叠
*/
collapsible?: boolean
/**
* 是否折叠
*/
collapsed?: boolean
/**
* 折叠切换
*/
onToggle?: () => void
}

if (__DEV__) {
Separator.displayName = 'SEPARATOR'
Expand Down
58 changes: 53 additions & 5 deletions packages/ui/resize-box/src/styles/resize-box.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,79 @@ $prefix: '#{$component-prefix}-resize-box' !default;
user-select: auto;

&-pane {
flex-grow: 0;
flex-shrink: 0;
display: flex;
justify-content: space-between;
overflow: hidden;

&--collapsed {
.#{$prefix}-separator-content {
width: 0;
border: none;
}

.#{$prefix}-toggle {
right: -13px;

svg[class^=hi-v4-icon] {
transform: rotate(180deg);
}
}
}
}

&-separator {
position: relative;
height: 100%;
padding: 0 2px;
cursor: col-resize;
flex-shrink: 0;
display: flex;
justify-content: center;
align-items: center;

&-content {
width: 2px;
box-sizing: border-box;
width: 6px;
height: 100%;
background-color: use-color('gray', 300);
border-right: 2px solid use-color('gray', 300);
}

&:hover {
.#{$prefix}-separator-content {
background-color: use-color('brandblue', 300);
border-right-color: use-color('brandblue', 300);
}

+ .#{$prefix}-toggle {
width: 10px;
}
}

&--collapsed {
width: 0;
cursor: default;
}
}

&-toggle {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
width: 12px;
height: 60px;
right: -11px;
top: 50%;
transform: translateY(-50%);
border-start-end-radius: 16px 8px;
border-end-end-radius: 16px 8px;
border: 1px solid use-color('gray', 300);
border-left: none;
cursor: pointer;
color: use-color('gray', 600);
background-color: #fff;

svg[class^=hi-v4-icon] {
margin-left: -4px;
}
}
}
51 changes: 51 additions & 0 deletions packages/ui/resize-box/stories/collapsible.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react'
import ResizeBox, { ResizeBoxPane } from '../src'

/**
* @title 设置是否可折叠
*/
export const Collapsible = () => {
const [collapsed, setCollapsed] = React.useState<boolean>(false)

return (
<>
<h1>Collapsible</h1>
<div className="resize-box-collapsible__wrap">
<ResizeBox style={{ width: 600, height: 400, border: '1px solid #ddd' }}>
<ResizeBoxPane
collapsible
collapsed={collapsed}
onCollapse={setCollapsed}
defaultWidth={200}
onResize={console.log}
>
<div
style={{
width: '100%',
overflow: 'auto',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}
>
left content
</div>
</ResizeBoxPane>
<ResizeBoxPane>
<div
style={{
width: '100%',
overflow: 'auto',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}
>
right content
</div>
</ResizeBoxPane>
</ResizeBox>
</div>
</>
)
}
1 change: 1 addition & 0 deletions packages/ui/resize-box/stories/index.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import ResizeBox from '../src'

export * from './basic.stories'
export * from './min-width.stories'
export * from './collapsible.stories'

export default {
title: 'Others/ResizeBox',
Expand Down

0 comments on commit a8444cc

Please sign in to comment.