@tiny-design/react 1.1.2 → 1.3.0

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 (64) hide show
  1. package/es/drawer/drawer.js +3 -2
  2. package/es/drawer/drawer.js.map +1 -1
  3. package/es/index.d.ts +2 -1
  4. package/es/index.js +2 -1
  5. package/es/message/message.js +3 -2
  6. package/es/message/message.js.map +1 -1
  7. package/es/modal/modal.js +3 -2
  8. package/es/modal/modal.js.map +1 -1
  9. package/es/overlay/overlay.js +2 -2
  10. package/es/overlay/overlay.js.map +1 -1
  11. package/es/transition/index.js +1 -0
  12. package/es/transition/index.js.map +1 -1
  13. package/es/transition/transition.d.ts +20 -5
  14. package/es/transition/transition.js +33 -8
  15. package/es/transition/transition.js.map +1 -1
  16. package/es/transition/use-transition.js +137 -0
  17. package/es/transition/use-transition.js.map +1 -0
  18. package/es/waterfall/hooks/use-breakpoint.js +47 -0
  19. package/es/waterfall/hooks/use-breakpoint.js.map +1 -0
  20. package/es/waterfall/hooks/use-positions.js +28 -0
  21. package/es/waterfall/hooks/use-positions.js.map +1 -0
  22. package/es/waterfall/index.d.ts +2 -0
  23. package/es/waterfall/index.js +9 -0
  24. package/es/waterfall/index.js.map +1 -0
  25. package/es/waterfall/style/_index.scss +22 -0
  26. package/es/waterfall/style/index.css +16 -0
  27. package/es/waterfall/style/index.d.ts +1 -0
  28. package/es/waterfall/style/index.js +1 -0
  29. package/es/waterfall/types.d.ts +35 -0
  30. package/es/waterfall/waterfall.d.ts +8 -0
  31. package/es/waterfall/waterfall.js +151 -0
  32. package/es/waterfall/waterfall.js.map +1 -0
  33. package/lib/drawer/drawer.js +5 -4
  34. package/lib/drawer/drawer.js.map +1 -1
  35. package/lib/index.d.ts +2 -1
  36. package/lib/index.js +2 -0
  37. package/lib/message/message.js +3 -2
  38. package/lib/message/message.js.map +1 -1
  39. package/lib/modal/modal.js +5 -4
  40. package/lib/modal/modal.js.map +1 -1
  41. package/lib/overlay/overlay.js +3 -3
  42. package/lib/overlay/overlay.js.map +1 -1
  43. package/lib/transition/index.js +1 -0
  44. package/lib/transition/index.js.map +1 -1
  45. package/lib/transition/transition.d.ts +20 -5
  46. package/lib/transition/transition.js +32 -7
  47. package/lib/transition/transition.js.map +1 -1
  48. package/lib/transition/use-transition.js +138 -0
  49. package/lib/transition/use-transition.js.map +1 -0
  50. package/lib/waterfall/hooks/use-breakpoint.js +49 -0
  51. package/lib/waterfall/hooks/use-breakpoint.js.map +1 -0
  52. package/lib/waterfall/hooks/use-positions.js +29 -0
  53. package/lib/waterfall/hooks/use-positions.js.map +1 -0
  54. package/lib/waterfall/index.js +8 -0
  55. package/lib/waterfall/index.js.map +1 -0
  56. package/lib/waterfall/style/_index.scss +22 -0
  57. package/lib/waterfall/style/index.css +16 -0
  58. package/lib/waterfall/style/index.d.ts +1 -0
  59. package/lib/waterfall/style/index.js +1 -0
  60. package/lib/waterfall/types.d.ts +35 -0
  61. package/lib/waterfall/waterfall.d.ts +8 -0
  62. package/lib/waterfall/waterfall.js +154 -0
  63. package/lib/waterfall/waterfall.js.map +1 -0
  64. package/package.json +3 -5
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["Transition"],"sources":["../../src/transition/index.tsx"],"sourcesContent":["import Transition from './transition';\n\nexport type { AnimationName, TransitionProps } from './transition';\nexport default Transition;\n"],"mappings":";AAGA,IAAA,gDAAeA"}
1
+ {"version":3,"file":"index.js","names":["Transition"],"sources":["../../src/transition/index.tsx"],"sourcesContent":["import Transition from './transition';\n\nexport type { AnimationName, TransitionProps } from './transition';\nexport { default as useTransition } from './use-transition';\nexport type { TransitionState, UseTransitionOptions, UseTransitionResult } from './use-transition';\nexport default Transition;\n"],"mappings":";;AAKA,IAAA,gDAAeA"}
@@ -1,16 +1,31 @@
1
1
  import React from "react";
2
- import { CSSTransitionProps } from "react-transition-group/CSSTransition";
3
2
 
4
3
  //#region src/transition/transition.d.ts
5
4
  type AnimationName = 'zoom-center-top' | 'zoom-center-bottom' | 'zoom-center-left' | 'zoom-center-right' | 'zoom-top-start' | 'zoom-top' | 'zoom-top-end' | 'zoom-bottom-start' | 'zoom-bottom' | 'zoom-bottom-end' | 'zoom-left-start' | 'zoom-left' | 'zoom-left-end' | 'zoom-right-start' | 'zoom-right' | 'zoom-right-end' | 'slide-up' | 'slide-down';
6
5
  type TransitionProps = {
7
- /** Animation prefix */prefix?: string; /** Preset animation name */
8
- animation?: AnimationName; /** Prevent the transition conflict with the inner component */
6
+ in?: boolean;
7
+ timeout?: number | {
8
+ enter: number;
9
+ exit: number;
10
+ };
11
+ appear?: boolean;
12
+ unmountOnExit?: boolean;
13
+ mountOnEnter?: boolean; /** Animation prefix */
14
+ prefix?: string; /** Preset animation name */
15
+ animation?: AnimationName; /** Custom class name base (overrides prefix + animation) */
16
+ classNames?: string; /** Prevent the transition conflict with the inner component */
9
17
  wrapper?: boolean;
18
+ nodeRef?: React.RefObject<HTMLElement | null>;
19
+ onEnter?: () => void;
20
+ onEntering?: () => void;
21
+ onEntered?: () => void;
22
+ onExit?: () => void;
23
+ onExiting?: () => void;
24
+ onExited?: () => void;
10
25
  children?: React.ReactNode;
11
- } & Partial<CSSTransitionProps<HTMLElement>>;
26
+ };
12
27
  declare const Transition: {
13
- (props: TransitionProps): React.ReactElement;
28
+ (props: TransitionProps): React.ReactElement | null;
14
29
  displayName: string;
15
30
  };
16
31
  //#endregion
@@ -1,20 +1,45 @@
1
1
  const require_runtime = require("../_virtual/_rolldown/runtime.js");
2
+ const require_use_transition = require("./use-transition.js");
2
3
  let react = require("react");
3
4
  react = require_runtime.__toESM(react);
4
5
  let react_jsx_runtime = require("react/jsx-runtime");
5
- let react_transition_group = require("react-transition-group");
6
6
  //#region src/transition/transition.tsx
7
+ function getTransitionClasses(base, state) {
8
+ switch (state) {
9
+ case "enter": return `${base}-enter`;
10
+ case "entering": return `${base}-enter ${base}-enter-active`;
11
+ case "entered": return `${base}-enter-done`;
12
+ case "exit": return `${base}-exit`;
13
+ case "exiting": return `${base}-exit ${base}-exit-active`;
14
+ case "exited": return `${base}-exit-done`;
15
+ default: return "";
16
+ }
17
+ }
7
18
  const Transition = (props) => {
8
- const { timeout = 300, unmountOnExit = true, appear = true, prefix = "ty", animation, classNames, nodeRef, children, wrapper, ...otherProps } = props;
9
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_transition_group.CSSTransition, {
10
- ...otherProps,
19
+ const { in: inProp = false, timeout = 300, unmountOnExit = true, mountOnEnter, appear = true, prefix = "ty", animation, classNames: classNamesProp, nodeRef, children, wrapper, onEnter, onEntering, onEntered, onExit, onExiting, onExited } = props;
20
+ const { state, shouldMount } = require_use_transition.default({
21
+ in: inProp,
11
22
  timeout,
12
23
  appear,
13
24
  unmountOnExit,
14
- nodeRef,
15
- classNames: classNames ? classNames : `${prefix}-${animation}`,
16
- children: wrapper ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children }) : children
25
+ mountOnEnter,
26
+ onEnter,
27
+ onEntering,
28
+ onEntered,
29
+ onExit,
30
+ onExiting,
31
+ onExited,
32
+ nodeRef
17
33
  });
34
+ if (!shouldMount) return null;
35
+ const transitionClasses = getTransitionClasses(classNamesProp ? classNamesProp : `${prefix}-${animation}`, state);
36
+ const child = wrapper ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children }) : children;
37
+ if (react.default.isValidElement(child)) {
38
+ const existingClassName = child.props.className || "";
39
+ const mergedClassName = existingClassName ? `${existingClassName} ${transitionClasses}`.trim() : transitionClasses;
40
+ return react.default.cloneElement(child, { className: mergedClassName || void 0 });
41
+ }
42
+ return child;
18
43
  };
19
44
  Transition.displayName = "Transition";
20
45
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"transition.js","names":["CSSTransition"],"sources":["../../src/transition/transition.tsx"],"sourcesContent":["import React from 'react';\nimport { CSSTransition } from 'react-transition-group';\nimport { CSSTransitionProps } from 'react-transition-group/CSSTransition';\n\nexport type AnimationName =\n | 'zoom-center-top'\n | 'zoom-center-bottom'\n | 'zoom-center-left'\n | 'zoom-center-right'\n | 'zoom-top-start'\n | 'zoom-top'\n | 'zoom-top-end'\n | 'zoom-bottom-start'\n | 'zoom-bottom'\n | 'zoom-bottom-end'\n | 'zoom-left-start'\n | 'zoom-left'\n | 'zoom-left-end'\n | 'zoom-right-start'\n | 'zoom-right'\n | 'zoom-right-end'\n | 'slide-up'\n | 'slide-down';\n\nexport type TransitionProps = {\n /** Animation prefix */\n prefix?: string;\n\n /** Preset animation name */\n animation?: AnimationName;\n\n /** Prevent the transition conflict with the inner component */\n wrapper?: boolean;\n children?: React.ReactNode;\n} & Partial<CSSTransitionProps<HTMLElement>>;\n\nconst Transition = (props: TransitionProps): React.ReactElement => {\n const {\n timeout = 300,\n unmountOnExit = true,\n appear = true,\n prefix = 'ty',\n animation,\n classNames,\n nodeRef,\n children,\n wrapper,\n ...otherProps\n } = props;\n\n return (\n <CSSTransition\n {...(otherProps as CSSTransitionProps<HTMLElement>)}\n timeout={timeout}\n appear={appear}\n unmountOnExit={unmountOnExit}\n nodeRef={nodeRef}\n classNames={classNames ? classNames : `${prefix}-${animation}`}>\n {wrapper ? <div>{children}</div> : (children as React.ReactElement)}\n </CSSTransition>\n );\n};\n\nTransition.displayName = 'Transition';\n\nexport default Transition;\n"],"mappings":";;;;;;AAoCA,MAAM,cAAc,UAA+C;CACjE,MAAM,EACJ,UAAU,KACV,gBAAgB,MAChB,SAAS,MACT,SAAS,MACT,WACA,YACA,SACA,UACA,SACA,GAAG,eACD;AAEJ,QACE,iBAAA,GAAA,kBAAA,KAACA,uBAAAA,eAAD;EACE,GAAK;EACI;EACD;EACO;EACN;EACT,YAAY,aAAa,aAAa,GAAG,OAAO,GAAG;YAClD,UAAU,iBAAA,GAAA,kBAAA,KAAC,OAAD,EAAM,UAAe,CAAA,GAAI;EACtB,CAAA;;AAIpB,WAAW,cAAc"}
1
+ {"version":3,"file":"transition.js","names":["useTransition","React"],"sources":["../../src/transition/transition.tsx"],"sourcesContent":["import React from 'react';\nimport useTransition, { TransitionState } from './use-transition';\n\nexport type AnimationName =\n | 'zoom-center-top'\n | 'zoom-center-bottom'\n | 'zoom-center-left'\n | 'zoom-center-right'\n | 'zoom-top-start'\n | 'zoom-top'\n | 'zoom-top-end'\n | 'zoom-bottom-start'\n | 'zoom-bottom'\n | 'zoom-bottom-end'\n | 'zoom-left-start'\n | 'zoom-left'\n | 'zoom-left-end'\n | 'zoom-right-start'\n | 'zoom-right'\n | 'zoom-right-end'\n | 'slide-up'\n | 'slide-down';\n\nexport type TransitionProps = {\n in?: boolean;\n timeout?: number | { enter: number; exit: number };\n appear?: boolean;\n unmountOnExit?: boolean;\n mountOnEnter?: boolean;\n\n /** Animation prefix */\n prefix?: string;\n\n /** Preset animation name */\n animation?: AnimationName;\n\n /** Custom class name base (overrides prefix + animation) */\n classNames?: string;\n\n /** Prevent the transition conflict with the inner component */\n wrapper?: boolean;\n\n nodeRef?: React.RefObject<HTMLElement | null>;\n\n onEnter?: () => void;\n onEntering?: () => void;\n onEntered?: () => void;\n onExit?: () => void;\n onExiting?: () => void;\n onExited?: () => void;\n\n children?: React.ReactNode;\n};\n\nfunction getTransitionClasses(base: string, state: TransitionState): string {\n switch (state) {\n case 'enter':\n return `${base}-enter`;\n case 'entering':\n return `${base}-enter ${base}-enter-active`;\n case 'entered':\n return `${base}-enter-done`;\n case 'exit':\n return `${base}-exit`;\n case 'exiting':\n return `${base}-exit ${base}-exit-active`;\n case 'exited':\n return `${base}-exit-done`;\n default:\n return '';\n }\n}\n\nconst Transition = (props: TransitionProps): React.ReactElement | null => {\n const {\n in: inProp = false,\n timeout = 300,\n unmountOnExit = true,\n mountOnEnter,\n appear = true,\n prefix = 'ty',\n animation,\n classNames: classNamesProp,\n nodeRef,\n children,\n wrapper,\n onEnter,\n onEntering,\n onEntered,\n onExit,\n onExiting,\n onExited,\n } = props;\n\n const { state, shouldMount } = useTransition({\n in: inProp,\n timeout,\n appear,\n unmountOnExit,\n mountOnEnter,\n onEnter,\n onEntering,\n onEntered,\n onExit,\n onExiting,\n onExited,\n nodeRef,\n });\n\n if (!shouldMount) {\n return null;\n }\n\n const base = classNamesProp ? classNamesProp : `${prefix}-${animation}`;\n const transitionClasses = getTransitionClasses(base, state);\n\n const child = wrapper ? <div>{children}</div> : children;\n\n if (React.isValidElement(child)) {\n const existingClassName = (child.props as { className?: string }).className || '';\n const mergedClassName = existingClassName\n ? `${existingClassName} ${transitionClasses}`.trim()\n : transitionClasses;\n\n return React.cloneElement(child as React.ReactElement<{ className?: string }>, {\n className: mergedClassName || undefined,\n });\n }\n\n return child as React.ReactElement;\n};\n\nTransition.displayName = 'Transition';\n\nexport default Transition;\n"],"mappings":";;;;;;AAsDA,SAAS,qBAAqB,MAAc,OAAgC;AAC1E,SAAQ,OAAR;EACE,KAAK,QACH,QAAO,GAAG,KAAK;EACjB,KAAK,WACH,QAAO,GAAG,KAAK,SAAS,KAAK;EAC/B,KAAK,UACH,QAAO,GAAG,KAAK;EACjB,KAAK,OACH,QAAO,GAAG,KAAK;EACjB,KAAK,UACH,QAAO,GAAG,KAAK,QAAQ,KAAK;EAC9B,KAAK,SACH,QAAO,GAAG,KAAK;EACjB,QACE,QAAO;;;AAIb,MAAM,cAAc,UAAsD;CACxE,MAAM,EACJ,IAAI,SAAS,OACb,UAAU,KACV,gBAAgB,MAChB,cACA,SAAS,MACT,SAAS,MACT,WACA,YAAY,gBACZ,SACA,UACA,SACA,SACA,YACA,WACA,QACA,WACA,aACE;CAEJ,MAAM,EAAE,OAAO,gBAAgBA,uBAAAA,QAAc;EAC3C,IAAI;EACJ;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;AAEF,KAAI,CAAC,YACH,QAAO;CAIT,MAAM,oBAAoB,qBADb,iBAAiB,iBAAiB,GAAG,OAAO,GAAG,aACP,MAAM;CAE3D,MAAM,QAAQ,UAAU,iBAAA,GAAA,kBAAA,KAAC,OAAD,EAAM,UAAe,CAAA,GAAG;AAEhD,KAAIC,MAAAA,QAAM,eAAe,MAAM,EAAE;EAC/B,MAAM,oBAAqB,MAAM,MAAiC,aAAa;EAC/E,MAAM,kBAAkB,oBACpB,GAAG,kBAAkB,GAAG,oBAAoB,MAAM,GAClD;AAEJ,SAAOA,MAAAA,QAAM,aAAa,OAAqD,EAC7E,WAAW,mBAAmB,KAAA,GAC/B,CAAC;;AAGJ,QAAO;;AAGT,WAAW,cAAc"}
@@ -0,0 +1,138 @@
1
+ require("../_virtual/_rolldown/runtime.js");
2
+ let react = require("react");
3
+ //#region src/transition/use-transition.ts
4
+ function normalizeTimeout(timeout) {
5
+ if (timeout == null) return {
6
+ enter: 300,
7
+ exit: 300
8
+ };
9
+ if (typeof timeout === "number") return {
10
+ enter: timeout,
11
+ exit: timeout
12
+ };
13
+ return timeout;
14
+ }
15
+ function useTransition(options) {
16
+ const { in: inProp, timeout, appear = true, unmountOnExit = true, mountOnEnter = false, onEnter, onEntering, onEntered, onExit, onExiting, onExited, nodeRef } = options;
17
+ const normalizedTimeout = normalizeTimeout(timeout);
18
+ const getInitialState = () => {
19
+ if (inProp) return appear ? "enter" : "entered";
20
+ if (unmountOnExit || mountOnEnter) return "unmounted";
21
+ return "exited";
22
+ };
23
+ const [state, setState] = (0, react.useState)(getInitialState);
24
+ const rafRef = (0, react.useRef)(0);
25
+ const timerRef = (0, react.useRef)(0);
26
+ const transitionEndRef = (0, react.useRef)(null);
27
+ const initialMountRef = (0, react.useRef)(true);
28
+ const callbacksRef = (0, react.useRef)({
29
+ onEnter,
30
+ onEntering,
31
+ onEntered,
32
+ onExit,
33
+ onExiting,
34
+ onExited
35
+ });
36
+ callbacksRef.current = {
37
+ onEnter,
38
+ onEntering,
39
+ onEntered,
40
+ onExit,
41
+ onExiting,
42
+ onExited
43
+ };
44
+ const cleanup = (0, react.useCallback)(() => {
45
+ if (rafRef.current) {
46
+ cancelAnimationFrame(rafRef.current);
47
+ rafRef.current = 0;
48
+ }
49
+ if (timerRef.current) {
50
+ clearTimeout(timerRef.current);
51
+ timerRef.current = 0;
52
+ }
53
+ if (transitionEndRef.current && nodeRef?.current) {
54
+ nodeRef.current.removeEventListener("transitionend", transitionEndRef.current);
55
+ transitionEndRef.current = null;
56
+ }
57
+ }, [nodeRef]);
58
+ const waitForTransition = (0, react.useCallback)((phase, done) => {
59
+ const safetyDuration = phase === "enter" ? normalizedTimeout.enter : normalizedTimeout.exit;
60
+ if (safetyDuration === 0) {
61
+ done();
62
+ return;
63
+ }
64
+ const finish = () => {
65
+ cleanup();
66
+ done();
67
+ };
68
+ const node = nodeRef?.current;
69
+ if (node) {
70
+ const handler = (e) => {
71
+ if (e.target === node) finish();
72
+ };
73
+ transitionEndRef.current = handler;
74
+ node.addEventListener("transitionend", handler);
75
+ }
76
+ timerRef.current = window.setTimeout(finish, safetyDuration + 50);
77
+ }, [
78
+ cleanup,
79
+ nodeRef,
80
+ normalizedTimeout.enter,
81
+ normalizedTimeout.exit
82
+ ]);
83
+ (0, react.useLayoutEffect)(() => {
84
+ if (state === "enter") {
85
+ callbacksRef.current.onEnter?.();
86
+ rafRef.current = requestAnimationFrame(() => {
87
+ setState("entering");
88
+ callbacksRef.current.onEntering?.();
89
+ waitForTransition("enter", () => {
90
+ setState("entered");
91
+ callbacksRef.current.onEntered?.();
92
+ });
93
+ });
94
+ }
95
+ }, [state]);
96
+ (0, react.useLayoutEffect)(() => {
97
+ if (state === "exit") {
98
+ callbacksRef.current.onExit?.();
99
+ rafRef.current = requestAnimationFrame(() => {
100
+ setState("exiting");
101
+ callbacksRef.current.onExiting?.();
102
+ waitForTransition("exit", () => {
103
+ setState((prev) => {
104
+ if (prev === "exiting") {
105
+ callbacksRef.current.onExited?.();
106
+ return unmountOnExit ? "unmounted" : "exited";
107
+ }
108
+ return prev;
109
+ });
110
+ });
111
+ });
112
+ }
113
+ }, [state]);
114
+ (0, react.useEffect)(() => {
115
+ if (initialMountRef.current) {
116
+ initialMountRef.current = false;
117
+ return;
118
+ }
119
+ if (inProp) {
120
+ cleanup();
121
+ setState("enter");
122
+ } else {
123
+ cleanup();
124
+ setState("exit");
125
+ }
126
+ }, [inProp]);
127
+ (0, react.useEffect)(() => {
128
+ return cleanup;
129
+ }, [cleanup]);
130
+ return {
131
+ state,
132
+ shouldMount: state !== "unmounted"
133
+ };
134
+ }
135
+ //#endregion
136
+ exports.default = useTransition;
137
+
138
+ //# sourceMappingURL=use-transition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-transition.js","names":[],"sources":["../../src/transition/use-transition.ts"],"sourcesContent":["import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';\n\nexport type TransitionState =\n | 'unmounted'\n | 'enter'\n | 'entering'\n | 'entered'\n | 'exit'\n | 'exiting'\n | 'exited';\n\nexport interface UseTransitionOptions {\n in: boolean;\n timeout?: number | { enter: number; exit: number };\n appear?: boolean;\n unmountOnExit?: boolean;\n mountOnEnter?: boolean;\n onEnter?: () => void;\n onEntering?: () => void;\n onEntered?: () => void;\n onExit?: () => void;\n onExiting?: () => void;\n onExited?: () => void;\n nodeRef?: React.RefObject<HTMLElement | null>;\n}\n\nexport interface UseTransitionResult {\n state: TransitionState;\n shouldMount: boolean;\n}\n\nfunction normalizeTimeout(\n timeout: number | { enter: number; exit: number } | undefined\n): { enter: number; exit: number } {\n if (timeout == null) return { enter: 300, exit: 300 };\n if (typeof timeout === 'number') return { enter: timeout, exit: timeout };\n return timeout;\n}\n\nfunction useTransition(options: UseTransitionOptions): UseTransitionResult {\n const {\n in: inProp,\n timeout,\n appear = true,\n unmountOnExit = true,\n mountOnEnter = false,\n onEnter,\n onEntering,\n onEntered,\n onExit,\n onExiting,\n onExited,\n nodeRef,\n } = options;\n\n const normalizedTimeout = normalizeTimeout(timeout);\n\n // Determine initial state\n const getInitialState = (): TransitionState => {\n if (inProp) {\n return appear ? 'enter' : 'entered';\n }\n if (unmountOnExit || mountOnEnter) {\n return 'unmounted';\n }\n return 'exited';\n };\n\n const [state, setState] = useState<TransitionState>(getInitialState);\n const rafRef = useRef<number>(0);\n const timerRef = useRef<number>(0);\n const transitionEndRef = useRef<(() => void) | null>(null);\n const initialMountRef = useRef(true);\n\n // Store latest callbacks in refs to avoid re-triggering effects\n const callbacksRef = useRef({\n onEnter,\n onEntering,\n onEntered,\n onExit,\n onExiting,\n onExited,\n });\n callbacksRef.current = {\n onEnter,\n onEntering,\n onEntered,\n onExit,\n onExiting,\n onExited,\n };\n\n const cleanup = useCallback(() => {\n if (rafRef.current) {\n cancelAnimationFrame(rafRef.current);\n rafRef.current = 0;\n }\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = 0;\n }\n if (transitionEndRef.current && nodeRef?.current) {\n nodeRef.current.removeEventListener('transitionend', transitionEndRef.current);\n transitionEndRef.current = null;\n }\n }, [nodeRef]);\n\n const waitForTransition = useCallback(\n (phase: 'enter' | 'exit', done: () => void) => {\n const safetyDuration =\n phase === 'enter' ? normalizedTimeout.enter : normalizedTimeout.exit;\n\n // timeout=0 means \"don't wait\" — advance immediately.\n // This matches react-transition-group's behavior where timeout=0\n // skips straight to the done state, letting CSS handle the animation\n // via the base element's transition property.\n if (safetyDuration === 0) {\n done();\n return;\n }\n\n const finish = () => {\n cleanup();\n done();\n };\n\n // Try transitionend listener first\n const node = nodeRef?.current;\n if (node) {\n const handler = (e: Event) => {\n if ((e as TransitionEvent).target === node) {\n finish();\n }\n };\n transitionEndRef.current = handler as () => void;\n node.addEventListener('transitionend', handler);\n }\n\n // Safety timeout fallback\n timerRef.current = window.setTimeout(finish, safetyDuration + 50);\n },\n [cleanup, nodeRef, normalizedTimeout.enter, normalizedTimeout.exit]\n );\n\n // Continue the enter animation after DOM is committed (state === 'enter').\n // useLayoutEffect fires synchronously after DOM mutations, guaranteeing\n // the child is mounted and refs are populated before onEnter runs.\n useLayoutEffect(() => {\n if (state === 'enter') {\n callbacksRef.current.onEnter?.();\n\n // Single rAF: useLayoutEffect runs before paint, so one rAF is enough\n // to ensure the browser has painted the -enter class before we add -enter-active.\n rafRef.current = requestAnimationFrame(() => {\n setState('entering');\n callbacksRef.current.onEntering?.();\n\n waitForTransition('enter', () => {\n setState('entered');\n callbacksRef.current.onEntered?.();\n });\n });\n }\n }, [state]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // Continue the exit animation after DOM reflects exit state\n useLayoutEffect(() => {\n if (state === 'exit') {\n callbacksRef.current.onExit?.();\n\n rafRef.current = requestAnimationFrame(() => {\n setState('exiting');\n callbacksRef.current.onExiting?.();\n\n waitForTransition('exit', () => {\n setState((prev) => {\n if (prev === 'exiting') {\n callbacksRef.current.onExited?.();\n return unmountOnExit ? 'unmounted' : 'exited';\n }\n return prev;\n });\n });\n });\n }\n }, [state]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // React to `in` prop changes — only set the initial phase state.\n // The useLayoutEffect above handles the rest after DOM commit.\n useEffect(() => {\n if (initialMountRef.current) {\n initialMountRef.current = false;\n // On initial mount with appear=true, state is already 'enter' from getInitialState,\n // and the useLayoutEffect above will pick it up.\n return;\n }\n\n if (inProp) {\n cleanup();\n setState('enter');\n } else {\n cleanup();\n setState('exit');\n }\n }, [inProp]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // Cleanup on unmount\n useEffect(() => {\n return cleanup;\n }, [cleanup]);\n\n const shouldMount = state !== 'unmounted';\n\n return { state, shouldMount };\n}\n\nexport default useTransition;\n"],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ require("../../_virtual/_rolldown/runtime.js");
2
+ let react = require("react");
3
+ //#region src/waterfall/hooks/use-breakpoint.ts
4
+ const breakpointMap = {
5
+ xxl: "(min-width: 1600px)",
6
+ xl: "(min-width: 1200px)",
7
+ lg: "(min-width: 992px)",
8
+ md: "(min-width: 768px)",
9
+ sm: "(min-width: 576px)",
10
+ xs: "(max-width: 575.98px)"
11
+ };
12
+ const responsiveArray = [
13
+ "xxl",
14
+ "xl",
15
+ "lg",
16
+ "md",
17
+ "sm",
18
+ "xs"
19
+ ];
20
+ function useBreakpoint() {
21
+ const [screens, setScreens] = (0, react.useState)({});
22
+ (0, react.useEffect)(() => {
23
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
24
+ const queries = /* @__PURE__ */ new Map();
25
+ const update = () => {
26
+ const next = {};
27
+ for (const bp of responsiveArray) {
28
+ const mql = queries.get(bp);
29
+ if (mql) next[bp] = mql.matches;
30
+ }
31
+ setScreens(next);
32
+ };
33
+ for (const bp of responsiveArray) {
34
+ const mql = window.matchMedia(breakpointMap[bp]);
35
+ queries.set(bp, mql);
36
+ mql.addEventListener("change", update);
37
+ }
38
+ update();
39
+ return () => {
40
+ for (const [, mql] of queries) mql.removeEventListener("change", update);
41
+ };
42
+ }, []);
43
+ return screens;
44
+ }
45
+ //#endregion
46
+ exports.default = useBreakpoint;
47
+ exports.responsiveArray = responsiveArray;
48
+
49
+ //# sourceMappingURL=use-breakpoint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-breakpoint.js","names":[],"sources":["../../../src/waterfall/hooks/use-breakpoint.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { Breakpoint } from '../types';\n\nconst breakpointMap: Record<Breakpoint, string> = {\n xxl: '(min-width: 1600px)',\n xl: '(min-width: 1200px)',\n lg: '(min-width: 992px)',\n md: '(min-width: 768px)',\n sm: '(min-width: 576px)',\n xs: '(max-width: 575.98px)',\n};\n\nconst responsiveArray: Breakpoint[] = ['xxl', 'xl', 'lg', 'md', 'sm', 'xs'];\n\ntype Screens = Partial<Record<Breakpoint, boolean>>;\n\nexport { responsiveArray };\n\nexport default function useBreakpoint(): Screens {\n const [screens, setScreens] = useState<Screens>({});\n\n useEffect(() => {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return;\n\n const queries = new Map<Breakpoint, MediaQueryList>();\n\n const update = () => {\n const next: Screens = {};\n for (const bp of responsiveArray) {\n const mql = queries.get(bp);\n if (mql) {\n next[bp] = mql.matches;\n }\n }\n setScreens(next);\n };\n\n for (const bp of responsiveArray) {\n const mql = window.matchMedia(breakpointMap[bp]);\n queries.set(bp, mql);\n mql.addEventListener('change', update);\n }\n\n update();\n\n return () => {\n for (const [, mql] of queries) {\n mql.removeEventListener('change', update);\n }\n };\n }, []);\n\n return screens;\n}\n"],"mappings":";;;AAGA,MAAM,gBAA4C;CAChD,KAAK;CACL,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACL;AAED,MAAM,kBAAgC;CAAC;CAAO;CAAM;CAAM;CAAM;CAAM;CAAK;AAM3E,SAAwB,gBAAyB;CAC/C,MAAM,CAAC,SAAS,eAAA,GAAA,MAAA,UAAgC,EAAE,CAAC;AAEnD,EAAA,GAAA,MAAA,iBAAgB;AACd,MAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,WAAY;EAE9E,MAAM,0BAAU,IAAI,KAAiC;EAErD,MAAM,eAAe;GACnB,MAAM,OAAgB,EAAE;AACxB,QAAK,MAAM,MAAM,iBAAiB;IAChC,MAAM,MAAM,QAAQ,IAAI,GAAG;AAC3B,QAAI,IACF,MAAK,MAAM,IAAI;;AAGnB,cAAW,KAAK;;AAGlB,OAAK,MAAM,MAAM,iBAAiB;GAChC,MAAM,MAAM,OAAO,WAAW,cAAc,IAAI;AAChD,WAAQ,IAAI,IAAI,IAAI;AACpB,OAAI,iBAAiB,UAAU,OAAO;;AAGxC,UAAQ;AAER,eAAa;AACX,QAAK,MAAM,GAAG,QAAQ,QACpB,KAAI,oBAAoB,UAAU,OAAO;;IAG5C,EAAE,CAAC;AAEN,QAAO"}
@@ -0,0 +1,29 @@
1
+ require("../../_virtual/_rolldown/runtime.js");
2
+ let react = require("react");
3
+ //#region src/waterfall/hooks/use-positions.ts
4
+ function usePositions(itemHeights, columnCount, verticalGutter) {
5
+ return (0, react.useMemo)(() => {
6
+ const columnHeights = new Array(columnCount).fill(0);
7
+ const positions = /* @__PURE__ */ new Map();
8
+ for (let i = 0; i < itemHeights.length; i += 1) {
9
+ const [itemKey, itemHeight, itemColumn] = itemHeights[i];
10
+ let targetColumn = itemColumn ?? columnHeights.indexOf(Math.min(...columnHeights));
11
+ targetColumn = Math.min(targetColumn, columnCount - 1);
12
+ const top = columnHeights[targetColumn];
13
+ positions.set(itemKey, {
14
+ column: targetColumn,
15
+ top
16
+ });
17
+ columnHeights[targetColumn] += itemHeight + verticalGutter;
18
+ }
19
+ return [positions, Math.max(0, Math.max(...columnHeights) - verticalGutter)];
20
+ }, [
21
+ columnCount,
22
+ itemHeights,
23
+ verticalGutter
24
+ ]);
25
+ }
26
+ //#endregion
27
+ exports.default = usePositions;
28
+
29
+ //# sourceMappingURL=use-positions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-positions.js","names":[],"sources":["../../../src/waterfall/hooks/use-positions.ts"],"sourcesContent":["import { useMemo } from 'react';\n\nexport type ItemHeightData = [key: React.Key, height: number, column?: number];\n\nexport type ItemPositions = Map<\n React.Key,\n {\n column: number;\n top: number;\n }\n>;\n\nexport default function usePositions(\n itemHeights: ItemHeightData[],\n columnCount: number,\n verticalGutter: number,\n): [ItemPositions, number] {\n return useMemo(() => {\n const columnHeights = new Array(columnCount).fill(0) as number[];\n const positions: ItemPositions = new Map();\n\n for (let i = 0; i < itemHeights.length; i += 1) {\n const [itemKey, itemHeight, itemColumn] = itemHeights[i];\n\n let targetColumn = itemColumn ?? columnHeights.indexOf(Math.min(...columnHeights));\n targetColumn = Math.min(targetColumn, columnCount - 1);\n\n const top = columnHeights[targetColumn];\n positions.set(itemKey, { column: targetColumn, top });\n\n columnHeights[targetColumn] += itemHeight + verticalGutter;\n }\n\n const totalHeight = Math.max(0, Math.max(...columnHeights) - verticalGutter);\n return [positions, totalHeight];\n }, [columnCount, itemHeights, verticalGutter]);\n}\n"],"mappings":";;;AAYA,SAAwB,aACtB,aACA,aACA,gBACyB;AACzB,SAAA,GAAA,MAAA,eAAqB;EACnB,MAAM,gBAAgB,IAAI,MAAM,YAAY,CAAC,KAAK,EAAE;EACpD,MAAM,4BAA2B,IAAI,KAAK;AAE1C,OAAK,IAAI,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK,GAAG;GAC9C,MAAM,CAAC,SAAS,YAAY,cAAc,YAAY;GAEtD,IAAI,eAAe,cAAc,cAAc,QAAQ,KAAK,IAAI,GAAG,cAAc,CAAC;AAClF,kBAAe,KAAK,IAAI,cAAc,cAAc,EAAE;GAEtD,MAAM,MAAM,cAAc;AAC1B,aAAU,IAAI,SAAS;IAAE,QAAQ;IAAc;IAAK,CAAC;AAErD,iBAAc,iBAAiB,aAAa;;AAI9C,SAAO,CAAC,WADY,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,cAAc,GAAG,eAAe,CAC7C;IAC9B;EAAC;EAAa;EAAa;EAAe,CAAC"}
@@ -0,0 +1,8 @@
1
+ require('../style/base.css');
2
+ require('./style/index.css');
3
+ //#region src/waterfall/index.tsx
4
+ var waterfall_default = require("./waterfall.js").default;
5
+ //#endregion
6
+ exports.default = waterfall_default;
7
+
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["Waterfall"],"sources":["../../src/waterfall/index.tsx"],"sourcesContent":["import Waterfall from './waterfall';\n\nexport type { WaterfallProps, WaterfallItem, Breakpoint } from './types';\n\nexport default Waterfall;\n"],"mappings":";AAIA,IAAA,8CAAeA"}
@@ -0,0 +1,22 @@
1
+ @use '../../style/variables' as *;
2
+
3
+ .#{$prefix}-waterfall {
4
+ position: relative;
5
+ width: 100%;
6
+ box-sizing: border-box;
7
+
8
+ &__item {
9
+ position: absolute;
10
+ box-sizing: border-box;
11
+ }
12
+
13
+ // Exit animation
14
+ &__item-fade-exit {
15
+ opacity: 1;
16
+ }
17
+
18
+ &__item-fade-exit-active {
19
+ opacity: 0;
20
+ transition: opacity 0.3s ease;
21
+ }
22
+ }
@@ -0,0 +1,16 @@
1
+ .ty-waterfall {
2
+ position: relative;
3
+ width: 100%;
4
+ box-sizing: border-box;
5
+ }
6
+ .ty-waterfall__item {
7
+ position: absolute;
8
+ box-sizing: border-box;
9
+ }
10
+ .ty-waterfall__item-fade-exit {
11
+ opacity: 1;
12
+ }
13
+ .ty-waterfall__item-fade-exit-active {
14
+ opacity: 0;
15
+ transition: opacity 0.3s ease;
16
+ }
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1 @@
1
+ require("./_index.css");
@@ -0,0 +1,35 @@
1
+ import { BaseProps } from "../_utils/props.js";
2
+ import React, { ReactNode } from "react";
3
+
4
+ //#region src/waterfall/types.d.ts
5
+ type Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl';
6
+ interface WaterfallItem<T = any> {
7
+ key: React.Key;
8
+ /** Pin this item to a specific column index */
9
+ column?: number;
10
+ /** Direct content — takes priority over itemRender */
11
+ children?: ReactNode;
12
+ /** Custom data passed to itemRender */
13
+ data?: T;
14
+ }
15
+ interface WaterfallProps<T = any> extends BaseProps, Omit<React.PropsWithRef<JSX.IntrinsicElements['div']>, 'children'> {
16
+ /** Number of columns, or responsive breakpoint config. Default: 3 */
17
+ columns?: number | Partial<Record<Breakpoint, number>>;
18
+ /** Spacing between items: number or [horizontal, vertical] */
19
+ gutter?: number | [number, number];
20
+ /** Array of items to render */
21
+ items?: WaterfallItem<T>[];
22
+ /** Custom render function for each item */
23
+ itemRender?: (item: WaterfallItem<T> & {
24
+ index: number;
25
+ column: number;
26
+ }) => ReactNode;
27
+ /** Callback when layout order changes */
28
+ onLayoutChange?: (sortInfo: {
29
+ key: React.Key;
30
+ column: number;
31
+ }[]) => void;
32
+ }
33
+ //#endregion
34
+ export { WaterfallProps };
35
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,8 @@
1
+ import { WaterfallProps } from "./types.js";
2
+ import React from "react";
3
+
4
+ //#region src/waterfall/waterfall.d.ts
5
+ declare const Waterfall: React.ForwardRefExoticComponent<Omit<WaterfallProps<any>, "ref"> & React.RefAttributes<HTMLDivElement>>;
6
+ //#endregion
7
+ export { Waterfall };
8
+ //# sourceMappingURL=waterfall.d.ts.map
@@ -0,0 +1,154 @@
1
+ const require_runtime = require("../_virtual/_rolldown/runtime.js");
2
+ const require_config_context = require("../config-provider/config-context.js");
3
+ const require_general = require("../_utils/general.js");
4
+ const require_index = require("../transition/index.js");
5
+ const require_use_breakpoint = require("./hooks/use-breakpoint.js");
6
+ const require_use_positions = require("./hooks/use-positions.js");
7
+ let react = require("react");
8
+ react = require_runtime.__toESM(react);
9
+ let classnames = require("classnames");
10
+ classnames = require_runtime.__toESM(classnames);
11
+ let react_jsx_runtime = require("react/jsx-runtime");
12
+ //#region src/waterfall/waterfall.tsx
13
+ const Waterfall = react.default.forwardRef((props, ref) => {
14
+ const { prefixCls: customisedCls, className, style, columns = 3, gutter = 0, items, itemRender, onLayoutChange, ...otherProps } = props;
15
+ const prefixCls = require_general.getPrefixCls("waterfall", (0, react.useContext)(require_config_context.ConfigContext).prefixCls, customisedCls);
16
+ const screens = require_use_breakpoint.default();
17
+ const columnCount = react.default.useMemo(() => {
18
+ if (typeof columns === "number") return columns;
19
+ const matchingBreakpoint = require_use_breakpoint.responsiveArray.find((bp) => screens[bp] && columns[bp] !== void 0);
20
+ if (matchingBreakpoint) return columns[matchingBreakpoint];
21
+ return columns.xs ?? 1;
22
+ }, [columns, screens]);
23
+ const [horizontalGutter, verticalGutter] = Array.isArray(gutter) ? gutter : [gutter, gutter];
24
+ const itemRefsMap = (0, react.useRef)(/* @__PURE__ */ new Map());
25
+ const setItemRef = (0, react.useCallback)((key, el) => {
26
+ itemRefsMap.current.set(key, el);
27
+ }, []);
28
+ const [itemHeights, setItemHeights] = (0, react.useState)([]);
29
+ const collectItemSizes = (0, react.useCallback)(() => {
30
+ if (!items || items.length === 0) {
31
+ setItemHeights([]);
32
+ return;
33
+ }
34
+ const nextHeights = items.map((item, index) => {
35
+ const key = item.key ?? index;
36
+ const el = itemRefsMap.current.get(key);
37
+ return [
38
+ key,
39
+ el ? el.getBoundingClientRect().height : 0,
40
+ item.column
41
+ ];
42
+ });
43
+ setItemHeights((prev) => {
44
+ return prev.length === nextHeights.length && prev.every((p, i) => p[0] === nextHeights[i][0] && p[1] === nextHeights[i][1]) ? prev : nextHeights;
45
+ });
46
+ }, [items]);
47
+ const [itemPositions, totalHeight] = require_use_positions.default(itemHeights, columnCount, verticalGutter);
48
+ (0, react.useEffect)(() => {
49
+ collectItemSizes();
50
+ }, [
51
+ items,
52
+ columnCount,
53
+ collectItemSizes
54
+ ]);
55
+ (0, react.useEffect)(() => {
56
+ if (typeof ResizeObserver === "undefined") return;
57
+ const observer = new ResizeObserver(() => {
58
+ collectItemSizes();
59
+ });
60
+ for (const [, el] of itemRefsMap.current) if (el) observer.observe(el);
61
+ return () => observer.disconnect();
62
+ }, [items, collectItemSizes]);
63
+ (0, react.useEffect)(() => {
64
+ if (!onLayoutChange || !items || items.length === 0) return;
65
+ if (!items.every((item) => itemPositions.has(item.key))) return;
66
+ onLayoutChange(items.map((item) => ({
67
+ key: item.key,
68
+ column: itemPositions.get(item.key).column
69
+ })));
70
+ }, [
71
+ itemPositions,
72
+ items,
73
+ onLayoutChange
74
+ ]);
75
+ const prevItemsRef = (0, react.useRef)(items || []);
76
+ const [exitingItems, setExitingItems] = (0, react.useState)(/* @__PURE__ */ new Map());
77
+ (0, react.useEffect)(() => {
78
+ const currentKeys = new Set((items || []).map((item, i) => item.key ?? i));
79
+ const removed = /* @__PURE__ */ new Map();
80
+ prevItemsRef.current.forEach((item, index) => {
81
+ const key = item.key ?? index;
82
+ if (!currentKeys.has(key)) removed.set(key, item);
83
+ });
84
+ if (removed.size > 0) setExitingItems((prev) => {
85
+ const next = new Map(prev);
86
+ removed.forEach((item, key) => next.set(key, item));
87
+ return next;
88
+ });
89
+ prevItemsRef.current = items || [];
90
+ }, [items]);
91
+ const handleExited = (0, react.useCallback)((key) => {
92
+ itemRefsMap.current.delete(key);
93
+ setExitingItems((prev) => {
94
+ const next = new Map(prev);
95
+ next.delete(key);
96
+ return next;
97
+ });
98
+ collectItemSizes();
99
+ }, [collectItemSizes]);
100
+ const cls = (0, classnames.default)(prefixCls, className);
101
+ const containerStyle = {
102
+ ...style,
103
+ position: "relative",
104
+ height: totalHeight || void 0
105
+ };
106
+ const mergedItems = items || [];
107
+ const renderItem = (item, index, isExiting) => {
108
+ const key = item.key ?? index;
109
+ const position = itemPositions.get(key);
110
+ const hasPosition = !!position;
111
+ const columnIndex = position?.column ?? 0;
112
+ const itemStyle = {
113
+ position: "absolute",
114
+ width: `calc((100% - ${horizontalGutter * (columnCount - 1)}px) / ${columnCount})`,
115
+ left: `calc((100% - ${horizontalGutter * (columnCount - 1)}px) / ${columnCount} * ${columnIndex} + ${horizontalGutter * columnIndex}px)`,
116
+ top: position?.top ?? 0,
117
+ transition: hasPosition ? "top 0.3s ease, left 0.3s ease, opacity 0.3s ease" : "none",
118
+ opacity: hasPosition ? 1 : 0
119
+ };
120
+ const content = item.children ?? itemRender?.({
121
+ ...item,
122
+ index,
123
+ column: columnIndex
124
+ });
125
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_index.default, {
126
+ in: !isExiting,
127
+ timeout: 300,
128
+ appear: false,
129
+ unmountOnExit: true,
130
+ classNames: `${prefixCls}__item-fade`,
131
+ onExited: () => handleExited(key),
132
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
133
+ ref: (el) => setItemRef(key, el),
134
+ className: `${prefixCls}__item`,
135
+ style: itemStyle,
136
+ children: content
137
+ })
138
+ }, key);
139
+ };
140
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
141
+ ref,
142
+ ...otherProps,
143
+ className: cls,
144
+ style: containerStyle,
145
+ onLoad: collectItemSizes,
146
+ onError: collectItemSizes,
147
+ children: [mergedItems.map((item, index) => renderItem(item, index, false)), Array.from(exitingItems.entries()).map(([key, item]) => renderItem(item, Number(key), true))]
148
+ });
149
+ });
150
+ Waterfall.displayName = "Waterfall";
151
+ //#endregion
152
+ exports.default = Waterfall;
153
+
154
+ //# sourceMappingURL=waterfall.js.map