@rainbow-oh/yee-c 0.14.3 → 0.14.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/cjs/Splitter/demo/animation.d.ts +3 -0
  2. package/dist/cjs/Splitter/demo/animation.js +43 -0
  3. package/dist/cjs/Splitter/hooks/useMove.d.ts +1 -1
  4. package/dist/cjs/Splitter/hooks/useMove.js +3 -1
  5. package/dist/cjs/Splitter/interface.d.ts +6 -0
  6. package/dist/cjs/Splitter/splitter-handler.d.ts +3 -1
  7. package/dist/cjs/Splitter/splitter-handler.js +3 -2
  8. package/dist/cjs/Splitter/splitter.js +73 -7
  9. package/dist/cjs/style/themes.css +1 -1
  10. package/dist/cjs/style/variables.css +1 -1
  11. package/dist/cjs/version.d.ts +1 -1
  12. package/dist/cjs/version.js +1 -1
  13. package/dist/esm/Splitter/demo/animation.d.ts +3 -0
  14. package/dist/esm/Splitter/demo/animation.js +51 -0
  15. package/dist/esm/Splitter/hooks/useMove.d.ts +1 -1
  16. package/dist/esm/Splitter/hooks/useMove.js +3 -1
  17. package/dist/esm/Splitter/interface.d.ts +6 -0
  18. package/dist/esm/Splitter/splitter-handler.d.ts +3 -1
  19. package/dist/esm/Splitter/splitter-handler.js +3 -2
  20. package/dist/esm/Splitter/splitter.js +96 -10
  21. package/dist/esm/style/themes.css +1 -1
  22. package/dist/esm/style/variables.css +1 -1
  23. package/dist/esm/version.d.ts +1 -1
  24. package/dist/esm/version.js +1 -1
  25. package/dist/umd/yee-c.min.js +1 -1
  26. package/package.json +1 -1
  27. package/src/Splitter/demo/animation.tsx +29 -0
  28. package/src/Splitter/hooks/useMove.ts +3 -0
  29. package/src/Splitter/index.en-US.md +2 -0
  30. package/src/Splitter/index.zh-CN.md +2 -0
  31. package/src/Splitter/interface.ts +6 -0
  32. package/src/Splitter/splitter-handler.tsx +4 -1
  33. package/src/Splitter/splitter.tsx +81 -2
  34. package/src/version.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rainbow-oh/yee-c",
3
- "version": "0.14.3",
3
+ "version": "0.14.4",
4
4
  "description": "yee basic components",
5
5
  "keywords": [
6
6
  "react",
@@ -0,0 +1,29 @@
1
+ import { Button, Splitter } from '@rainbow-oh/yee-c';
2
+ import React from 'react';
3
+
4
+ export default () => {
5
+ const [open, setOpen] = React.useState(false);
6
+
7
+ return (
8
+ <div>
9
+ <div style={{ marginBottom: 8 }}>
10
+ <Button type="primary" onClick={() => setOpen((v) => !v)}>
11
+ {open ? 'Close Panel' : 'Open Panel'}
12
+ </Button>
13
+ </div>
14
+ <div style={{ height: 300 }}>
15
+ <Splitter animation>
16
+ <Splitter.Item min="30%">
17
+ <div style={{ padding: 16, backgroundColor: '#f0f0f0' }}>Content Panel</div>
18
+ </Splitter.Item>
19
+ {open ? (
20
+ // 条件渲染的面板:挂载时从 0 展开到目标尺寸,卸载时过渡收合到 0 后移除
21
+ <Splitter.Item size="40%" min="200px" max="60%">
22
+ <div style={{ padding: 16, backgroundColor: '#e0e0e0' }}>Assistant Panel</div>
23
+ </Splitter.Item>
24
+ ) : null}
25
+ </Splitter>
26
+ </div>
27
+ </div>
28
+ );
29
+ };
@@ -5,6 +5,7 @@ export default function useMove(
5
5
  handler: React.RefObject<HTMLDivElement | null>,
6
6
  direction: 'horizontal' | 'vertical',
7
7
  onMove: (dis: number) => void,
8
+ onMovingChange?: (moving: boolean) => void,
8
9
  ) {
9
10
  const [moving, setMoving] = React.useState(false);
10
11
 
@@ -20,6 +21,7 @@ export default function useMove(
20
21
  let x = e.pageX;
21
22
  let y = e.pageY;
22
23
  setMoving(true);
24
+ onMovingChange?.(true);
23
25
 
24
26
  const onMouseMove = debounce((e: MouseEvent) => {
25
27
  document.body.style.userSelect = 'none';
@@ -38,6 +40,7 @@ export default function useMove(
38
40
 
39
41
  const onMouseUp = () => {
40
42
  setMoving(false);
43
+ onMovingChange?.(false);
41
44
  window.removeEventListener('mousemove', onMouseMove);
42
45
  window.removeEventListener('mouseup', onMouseUp);
43
46
  ele.removeEventListener('mouseup', onMouseUp);
@@ -21,6 +21,7 @@ Split panels for layout.
21
21
  <code src="./demo/size.tsx" title="Size Control" description="Control panel size"></code>
22
22
  <code src="./demo/nested.tsx" title="Nested" description="Nested splitters"></code>
23
23
  <code src="./demo/bordered.tsx" title="Bordered" description="Splitter with borders"></code>
24
+ <code src="./demo/animation.tsx" title="Animation" description="With animation enabled, conditionally rendered panels mount/unmount and programmatic size changes transition smoothly; dragging stays real-time"></code>
24
25
 
25
26
  ## API
26
27
 
@@ -36,6 +37,7 @@ Split panels for layout.
36
37
  | bordered | `boolean` | Display border or not | - |
37
38
  | itemPadding | `number \| string` | Item padding | - |
38
39
  | onResize | `(sizes: number[]) => void` | Panel size change event | - |
40
+ | animation | `boolean` | Animate programmatic size changes, collapsible expand/collapse and panel mount/unmount with a flex-basis transition; dragging always tracks the pointer in real time | `false` |
39
41
  | data-* | `string` | - | Supports all data-* attributes, forwarded to root DOM element |
40
42
 
41
43
  ### SplitterItemProps
@@ -21,6 +21,7 @@ toc: 'content'
21
21
  <code src="./demo/size.tsx" title="尺寸控制" description="控制面板尺寸"></code>
22
22
  <code src="./demo/nested.tsx" title="嵌套" description="嵌套分割面板"></code>
23
23
  <code src="./demo/bordered.tsx" title="边框" description="带边框的分割面板"></code>
24
+ <code src="./demo/animation.tsx" title="过渡动画" description="开启 animation 后,条件渲染面板的挂载/卸载与受控尺寸变更有平滑过渡动画;拖拽缩放始终实时跟手"></code>
24
25
 
25
26
  ## API
26
27
 
@@ -36,6 +37,7 @@ toc: 'content'
36
37
  | bordered | `boolean` | 是否显示边框 | - |
37
38
  | itemPadding | `number \| string` | 项目内边距 | - |
38
39
  | onResize | `(sizes: number[]) => void` | 面板尺寸变化事件 | - |
40
+ | animation | `boolean` | 受控尺寸变更、collapsible 收展及面板挂载/卸载时的 flex-basis 过渡动画;拖拽缩放始终实时跟手,不受影响 | `false` |
39
41
  | data-* | `string` | - | 支持所有 data-* 属性,透传到组件根 DOM 元素 |
40
42
 
41
43
  ### SplitterItemProps
@@ -82,4 +82,10 @@ export interface SplitterProps extends DataAttributeProps {
82
82
  * panel size change event
83
83
  * */
84
84
  onResize?: (sizes: PanelRect[]) => void;
85
+ /**
86
+ * animate programmatic size changes and panel mount/unmount with a
87
+ * flex-basis transition; dragging always tracks the pointer in real time
88
+ * @default false
89
+ */
90
+ animation?: boolean;
85
91
  }
@@ -18,6 +18,8 @@ interface SplitterHandlerProps {
18
18
  size: { startSize: number; endSize: number },
19
19
  eles: { start: HTMLDivElement; end: HTMLDivElement },
20
20
  ) => void;
21
+ /** 拖拽开始/结束回调(animation 模式下拖拽期间需关闭 flex-basis 过渡) */
22
+ onDraggingChange?: (dragging: boolean) => void;
21
23
  }
22
24
 
23
25
  const SplitterHandler = ({
@@ -28,6 +30,7 @@ const SplitterHandler = ({
28
30
  records,
29
31
  setRecords,
30
32
  onClick,
33
+ onDraggingChange,
31
34
  }: SplitterHandlerProps) => {
32
35
  // eslint-disable-line @typescript-eslint/no-unused-vars
33
36
  const { start, end } = collapsible
@@ -91,7 +94,7 @@ const SplitterHandler = ({
91
94
  });
92
95
  };
93
96
 
94
- useMove(handler, layout, onMove);
97
+ useMove(handler, layout, onMove, onDraggingChange);
95
98
 
96
99
  const handleClick = (action: 'collapse' | 'expand') => {
97
100
  const eles = refs.current;
@@ -139,7 +139,15 @@ const getPanelSize = (
139
139
  return panelsRect;
140
140
  };
141
141
 
142
- const Wrapper = ({ children, itemsSize, onClick, ...props }: any) => {
142
+ const Wrapper = ({
143
+ children,
144
+ itemsSize,
145
+ itemTransition,
146
+ animation,
147
+ onDraggingChange,
148
+ onClick,
149
+ ...props
150
+ }: any) => {
143
151
  const wrappered = [] as React.ReactElement[];
144
152
  const refs = useRef<
145
153
  Record<
@@ -171,6 +179,8 @@ const Wrapper = ({ children, itemsSize, onClick, ...props }: any) => {
171
179
  style: {
172
180
  ...style, // If there is a border or other size-related design, it may cause calculation issues
173
181
  flexBasis: currentSize,
182
+ // animation 模式下受控尺寸变更走 flex-basis 过渡;拖拽期间为 undefined 保证跟手
183
+ transition: itemTransition,
174
184
  },
175
185
  min,
176
186
  max,
@@ -212,15 +222,55 @@ const Wrapper = ({ children, itemsSize, onClick, ...props }: any) => {
212
222
  flexable={flexable}
213
223
  expandable={_expandable}
214
224
  onClick={onClick}
225
+ onDraggingChange={onDraggingChange}
215
226
  key={`handler-${n}`}
216
227
  />,
217
228
  );
218
229
  }
219
230
  });
220
231
 
232
+ // ── 出场动画:children 数量减少(条件渲染的面板卸载)时,保留上一次渲染的
233
+ // 尾部面板克隆,flex-basis 过渡到 0 后再真正移除,避免右栏瞬间消失导致跳变。
234
+ const prevCountRef = React.useRef(arr.length);
235
+ const prevClonesRef = React.useRef<React.ReactElement[]>([]);
236
+ const [exiting, setExiting] = React.useState<React.ReactElement<any>[]>([]);
237
+
238
+ React.useEffect(() => {
239
+ const prevCount = prevCountRef.current;
240
+ prevCountRef.current = arr.length;
241
+ if (!animation || arr.length >= prevCount) {
242
+ return;
243
+ }
244
+ // 上一帧克隆里已带原 flex-basis(本帧 records 已不含被移除面板)
245
+ const removed = prevClonesRef.current.slice(arr.length);
246
+ if (!removed.length) {
247
+ return;
248
+ }
249
+ setExiting(removed);
250
+ // 第二阶段:下一帧 flex-basis 归 0,触发过渡
251
+ const raf = requestAnimationFrame(() => {
252
+ setExiting((list) =>
253
+ list.map((el) => React.cloneElement(el, { style: { ...el.props.style, flexBasis: 0 } })),
254
+ );
255
+ });
256
+ const timer = window.setTimeout(() => setExiting([]), 320);
257
+ return () => {
258
+ // 过渡期间面板重新打开:立即丢弃残留克隆
259
+ cancelAnimationFrame(raf);
260
+ window.clearTimeout(timer);
261
+ setExiting([]);
262
+ };
263
+ }, [arr.length, animation]);
264
+
265
+ // 记录本帧克隆,供下一次渲染的移除检测读取(声明在其后:上方 effect 读到的是旧值)
266
+ React.useEffect(() => {
267
+ prevClonesRef.current = wrappered;
268
+ });
269
+
221
270
  return (
222
271
  <SplitterWrapperCtx.Provider value={{ refs }}>
223
272
  {wrappered}
273
+ {exiting}
224
274
  </SplitterWrapperCtx.Provider>
225
275
  );
226
276
  };
@@ -236,6 +286,7 @@ const Splitter = React.forwardRef(
236
286
  children,
237
287
  bordered,
238
288
  onResize,
289
+ animation,
239
290
  ...rest
240
291
  } = props;
241
292
 
@@ -245,6 +296,15 @@ const Splitter = React.forwardRef(
245
296
 
246
297
  const [records, setRecords] = useState<PanelRect[]>([]);
247
298
 
299
+ // animation 模式:拖拽期间关掉面板的 flex-basis 过渡(onMove 直接写 DOM,过渡会导致跟不
300
+ // 上鼠标);其余程序性尺寸变更(受控 size、collapsible 收展、面板增减)都走过渡。
301
+ const [dragging, setDragging] = React.useState(false);
302
+ const itemTransition = animation
303
+ ? dragging
304
+ ? undefined
305
+ : 'flex-basis 0.28s cubic-bezier(0.22, 0.61, 0.36, 1)'
306
+ : undefined;
307
+
248
308
  const handleClick = (index: number, action: 'expand' | 'collapse') => {
249
309
  const c = index - 1;
250
310
 
@@ -303,15 +363,31 @@ const Splitter = React.forwardRef(
303
363
  }
304
364
  }, [layout]);
305
365
 
366
+ const prevPanelCountRef = React.useRef(-1);
367
+
306
368
  React.useEffect(() => {
307
369
  if (totalSize) {
308
370
  const panelsSize = getPanelSize(children, {
309
371
  totalSize: totalSize,
310
372
  layout,
311
373
  });
374
+ // animation 模式下面板新增(条件渲染的面板挂载)时先以 0 尺寸入库,下一帧再落到
375
+ // 目标尺寸,配合 flex-basis 过渡播放入场动画;否则新面板瞬间撑开、旧面板瞬间收窄。
376
+ const prevCount = prevPanelCountRef.current;
377
+ prevPanelCountRef.current = panelsSize.length;
378
+ if (animation && prevCount >= 0 && panelsSize.length > prevCount) {
379
+ panelsSize.slice(prevCount).forEach((item) => {
380
+ item.currentSize = 0;
381
+ });
382
+ setRecords(panelsSize);
383
+ const raf = requestAnimationFrame(() => {
384
+ setRecords(getPanelSize(children, { totalSize: totalSize, layout }));
385
+ });
386
+ return () => cancelAnimationFrame(raf);
387
+ }
312
388
  setRecords(panelsSize);
313
389
  }
314
- }, [children, totalSize, layout]);
390
+ }, [children, totalSize, layout, animation]);
315
391
 
316
392
  return (
317
393
  <div className={cls} {...rest} ref={componentRef}>
@@ -323,6 +399,9 @@ const Splitter = React.forwardRef(
323
399
  totalSize={totalSize}
324
400
  itemsSize={records}
325
401
  onClick={handleClick}
402
+ animation={animation}
403
+ itemTransition={itemTransition}
404
+ onDraggingChange={setDragging}
326
405
  >
327
406
  {children}
328
407
  </Wrapper>
package/src/version.ts CHANGED
@@ -4,7 +4,7 @@ declare global {
4
4
  }
5
5
  }
6
6
 
7
- export const version = "0.14.3";
7
+ export const version = "0.14.4";
8
8
 
9
9
  if (typeof window !== 'undefined') {
10
10
  window.__yee_c_version__ = version;