跳转到内容

Resizable 可调容器

通过拖拽或键盘调整内容区域的尺寸。

用法

从右侧、底部或右下角调整尺寸

组件结构

加粗的是必需部件。

data-scope="resizable"root · handle

示例

全部边缘

从任意边缘或角点调整尺寸

约束

设置宽高比和步进

16:9 宽高比

240 × 135

40px 步进

240 × 120

禁用

禁止调整尺寸

设计指引

何时使用

  • 调整侧栏、卡片或编辑器预览区域。
  • 保存用户设置的内容尺寸。

何时不用

特性

  • 支持八个方向的调整把手。
  • 把手命中区附着在容器内部,边缘指示条贴住容器边框,角部形状继承容器圆角。
  • 支持最小/最大尺寸、宽高比和步进约束。
  • 支持方向键、Home、End 和 Escape。
  • 调整中和调整结束分别提供回调。

组合

最佳实践

  • 提供合理的最小与最大尺寸,避免内容被压到不可读。
  • 记住用户调整后的尺寸,下次打开时还原。
  • 把手留足命中区,粗指针下用伪元素扩展。

反模式

  • 每一帧调整都请求服务端或重排整页。
  • 把手只在悬停时出现,键盘用户无法找到入口。

API 参考

产物

自定义元素<xh-resizable>
Vue 组件XhResizableHandle XhResizableRoot
组合式函数useResizable
状态机resizableMachine
皮肤@xihan-ui/styles/resizable.css

Props

属性类型必填说明
dimensionsResizableDimensions受控尺寸。提供后由外部决定,内部只发意图。
defaultDimensionsResizableDimensions
minWidthnumber
minHeightnumber
maxWidthnumber
maxHeightnumber
aspectRationumber宽高比(宽 ÷ 高)。提供后锁定;四条边各按自身的轴计算另一轴,四个角以宽为准。
stepnumber吸附步进:宽高各自落到最近的整数倍。
keyboardStepnumber方向键一次推动的距离(px),默认 8。
keyboardLargeStepnumber按住 Shift 时的步长(px),默认 40。
edgesResizeEdge[]允许调整的边,默认八向全部开启。 只提供东南两向即只能向右下角撑大,那是文档流中最常见的形态。
disabledboolean
dirDirection
translationsPartial<ResizableTranslations>
onDimensionsChange(details: ResizableDimensionsChangeDetails) => void尺寸变化意图。拖动途中连续发出。
onDimensionsChangeEnd(details: ResizableDimensionsChangeEndDetails) => void一次调整收尾时发出一次。保存尺寸使用它,不使用 onDimensionsChange。

事件

自定义元素将载荷放在 detail;Vue 使用同名 emit。

事件载荷说明
dimensions-changeResizableDimensionsChangeDetails尺寸变化(拖动途中连续发出);detail 为 { dimensions }
dimensions-change-endResizableDimensionsChangeEndDetails一次调整收尾时发出一次;detail 为 { dimensions, edge }

插槽

仅列出带载荷的插槽。

Vue 组件插槽载荷说明
XhResizableHandledefault
XhResizableRootdefaultResizableRootSlotProps

状态

以下名称仅用于内部状态机。

状态idle · resizing

事件RESIZE.START · RESIZE.MOVE · RESIZE.END · RESIZE.CANCEL · RESIZE.NUDGE · RESIZE.TO_BOUND · DIMENSIONS.SET

判据canResize

connect API

getXxxProps() 返回对应部件的宿主属性。

成员类型说明
dimensionsResizableDimensions
offsetResizableOffset
resizingboolean正在调整(拖动中)。键盘推动一步不计。
activeEdgeResizeEdge | null
disabledboolean
edgeEnabled(edge: ResizeEdge) => boolean该边是否开放。
setDimensions(dimensions: ResizableDimensions) => void整份赋值:先经约束再落定。
getRootProps() => T['element']
getHandleProps(props: { edge: ResizeEdge }) => T['element']

无障碍

键盘

规格出处:W3C APG

按键生效条件行为
ArrowRight / ArrowDownfocus in handle, not disabled按屏幕方向推动该边一步(默认 8px):推东边是变宽、推西边是变窄,与拖动同义。按的是屏幕方向,rtl 下两键不对调:此时改由行尾侧的边落在屏幕左边来体现
ArrowLeft / ArrowUpfocus in handle, not disabled往反方向推一步,规则同上
Shift+ArrowRight / Shift+ArrowLeft / Shift+ArrowUp / Shift+ArrowDownfocus in handle, not disabled按大步长推(默认 40px)
Homefocus in handle, not disabled把这条边推到它眼下能到的最小尺寸
Endfocus in handle, not disabled推到最大尺寸;未提供上限时不动
Escape调整中放弃这一次调整,尺寸与位移退回按下那一刻;收尾回调不发

ARIA

以下属性由 connect 生成。

部件属性
rootaria-labeltranslations?.root
rootrole'group'
handlearia-disabled'false' | 'true'
handlearia-labeltranslations?.handle?.(edge)
handlearia-orientation'horizontal' | 'vertical'
handlearia-valuenowMath.round(edge === 'n' || edge === 's' ? dimensions.…
handlerole'separator'

样式参考

皮肤

@xihan-ui/styles/resizable.css 使用 [data-scope="resizable"][data-part="root"] 部件选择器,位于 xihan.components 层。覆盖样式使用 xihan.overrides

forced-colors: active 下另有一套规则:颜色交给系统,边框与状态标记改用系统色关键字。

数据属性

connect 生成;条件不成立时不输出无值属性。

部件属性
rootdata-disabled''(条件成立时才出现)
rootdata-edgecontext.get('activeEdge')
rootdata-resizing''(条件成立时才出现)
handledata-disabled''(条件成立时才出现)
handledata-edgeedge
handledata-resizing''(条件成立时才出现)

CSS 变量

本组件公开覆盖槽由独立皮肤的实际消费位生成;默认来源、作用部件和状态均与 CSS 同源。

变量部件CSS 属性状态默认来源说明
--xh-resizable-cornerhandleblock-size
inline-size
edge=ne
edge=nw
edge=se
edge=sw
--xh-space-4resizable 的 handle 部件 block-size、inline-size 覆盖槽。
--xh-resizable-corner-insethandleinset-block-end
inset-block-start
inset-inline-end
inset-inline-start
edge=ne
edge=nw
edge=se
edge=sw
0resizable 的 handle 部件 inset-block-end、inset-block-start、inset-inline-end、inset-inline-start 覆盖槽。
--xh-resizable-griphandleblock-size
inline-size
inset-block
inset-inline
edge=e
edge=n
edge=s
edge=w
--xh-space-2resizable 的 handle 部件 block-size、inline-size、inset-block、inset-inline 覆盖槽。
--xh-resizable-handle-bghandlebackground
border
default
edge=ne
edge=nw
edge=se
edge=sw
is([data-edge='ne'], [data-edge='nw'], [data-edge='se'], [data-edge='sw'])
--xh-fg-defaultresizable 的 handle 部件 background、border 覆盖槽。
--xh-resizable-handle-bg-activehandlebackground
border
edge=ne
edge=nw
edge=se
edge=sw
is([data-edge='ne'], [data-edge='nw'], [data-edge='se'], [data-edge='sw'])
resizing
--xh-bg-brandresizable 的 handle 部件 background、border 覆盖槽。
--xh-resizable-handle-bg-hoverhandlebackground
border
edge=ne
edge=nw
edge=se
edge=sw
hover
is([data-edge='ne'], [data-edge='nw'], [data-edge='se'], [data-edge='sw'])
--xh-fg-defaultresizable 的 handle 部件 background、border 覆盖槽。
--xh-resizable-handle-radiushandleborder-radiusdefault--xh-shape-pillresizable 的 handle 部件 border-radius 覆盖槽。
--xh-resizable-indicator-insethandleinset-block-end
inset-block-start
inset-inline-end
inset-inline-start
edge=e
edge=n
edge=s
edge=w
0resizable 的 handle 部件 inset-block-end、inset-block-start、inset-inline-end、inset-inline-start 覆盖槽。
--xh-resizable-indicator-lengthhandleblock-size
inline-size
edge=e
edge=n
edge=s
edge=w
is([data-edge='e'], [data-edge='w'])
is([data-edge='n'], [data-edge='s'])
--xh-space-8resizable 的 handle 部件 block-size、inline-size 覆盖槽。
--xh-resizable-indicator-thicknesshandleblock-size
border-block-end-width
border-block-start-width
border-inline-end-width
border-inline-start-width
inline-size
edge=e
edge=n
edge=ne
edge=nw
edge=s
edge=se
edge=sw
edge=w
is([data-edge='e'], [data-edge='w'])
is([data-edge='n'], [data-edge='s'])
--xh-stroke-strongresizable 的 handle 部件 block-size、border-block-end-width、border-block-start-width、border-inline-end-width、border-inline-start-width、inline-size 覆盖槽。

动效

background · border-colortransition 过渡。时长与缓动读动效令牌,改令牌即改全局节奏。

系统开启减弱动效时由令牌层统一收敛,皮肤不另作判断。

RTL

皮肤用逻辑属性排布(inline-start 一族),dir="rtl" 下自动镜像。

  • 边缘使用逻辑方向,自动适配 RTL。
  • 键盘方向始终对应屏幕方向。

Released under The MIT License