deepsea-components 3.1.1 → 3.2.0-beta.20240606184051

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.
@@ -1,4 +1,4 @@
1
- import { CSSProperties, HTMLAttributes, Key, ReactNode } from "react";
1
+ import { CSSProperties, HTMLAttributes, Key, ReactNode, RefObject } from "react";
2
2
  export interface FlowSizeData {
3
3
  /** 容器宽度 */
4
4
  width: number;
@@ -68,6 +68,7 @@ export interface FlowProps<T> extends Omit<HTMLAttributes<HTMLDivElement>, "chil
68
68
  transitionDuration?: number;
69
69
  /** 变化的回调函数 */
70
70
  onSizeChange?: (sizeData: FlowSizeData) => void;
71
+ element?: RefObject<HTMLDivElement>;
71
72
  }
72
73
  export declare function getGapRange(gap?: undefined | number | "auto" | (number | "auto")[]): [number, number | "auto"];
73
74
  export declare function getGapCountAndSize(width: number, itemWidth: number, minGap: number, maxGap: number | "auto"): [number, number];
@@ -26,6 +26,7 @@ __export(Flow_exports, {
26
26
  });
27
27
  module.exports = __toCommonJS(Flow_exports);
28
28
  var import_css = require("@emotion/css");
29
+ var import_ahooks = require("ahooks");
29
30
  var import_deepsea_tools = require("deepsea-tools");
30
31
  var import_react = require("react");
31
32
  var import_utils = require("../utils");
@@ -47,15 +48,13 @@ function getGapCountAndSize(width, itemWidth, minGap, maxGap) {
47
48
  return [count, maxGap];
48
49
  return [count, averageGap];
49
50
  }
50
- function Flow(props) {
51
- let { itemWidth, itemHeight, columnGap, rowGap, maxRows, data, render, keyExactor, className, style, wrapperClassName, wrapperStyle, throttle, transitionDuration, onSizeChange, containerClassName, containerStyle, gap = 0, ...rest } = props;
51
+ function RealFlow(props) {
52
+ let { itemWidth, itemHeight, columnGap, rowGap, maxRows, data, render, keyExactor, className, style, wrapperClassName, wrapperStyle, throttle, transitionDuration, onSizeChange, containerClassName, containerStyle, gap = 0, width, element, ...rest } = props;
52
53
  rowGap ??= gap;
53
54
  columnGap ??= gap;
54
55
  const [minColumnGap, maxColumnGap] = getGapRange(columnGap);
55
- const [width, setWidth] = (0, import_react.useState)(0);
56
56
  const [columnCount, setColumnCount] = (0, import_react.useState)(1);
57
57
  const [columnGapSize, setColumnGapSize] = (0, import_react.useState)(minColumnGap);
58
- const [showItems, setShowItems] = (0, import_react.useState)(false);
59
58
  const ele = (0, import_react.useRef)(null);
60
59
  const contentRows = Math.ceil(data.length / columnCount);
61
60
  const contentShownRows = typeof maxRows === "number" ? Math.min(contentRows, maxRows) : contentRows;
@@ -73,29 +72,21 @@ function Flow(props) {
73
72
  return false;
74
73
  return index >= maxRows * columnCount;
75
74
  }
75
+ function task() {
76
+ const [newColumnCount, newColumnGapSize] = getGapCountAndSize(width, itemWidth, minColumnGap, maxColumnGap);
77
+ setColumnCount(newColumnCount);
78
+ setColumnGapSize(newColumnGapSize);
79
+ }
80
+ (0, import_react.useImperativeHandle)(element, () => ele.current, [ele.current]);
76
81
  (0, import_react.useEffect)(() => {
77
- let timeout;
78
- const observer = new ResizeObserver((entries) => {
79
- clearTimeout(timeout);
80
- function task() {
81
- const { width: width2 } = entries[0].contentRect;
82
- const [newColumnCount, newColumnGapSize] = getGapCountAndSize(width2, itemWidth, minColumnGap, maxColumnGap);
83
- setShowItems(true);
84
- setWidth(width2);
85
- setColumnCount(newColumnCount);
86
- setColumnGapSize(newColumnGapSize);
87
- }
88
- if (throttle === 0) {
89
- task();
90
- } else {
91
- timeout = window.setTimeout(task, throttle || 200);
92
- }
93
- });
94
- observer.observe(ele.current);
95
- return () => {
96
- observer.disconnect();
97
- };
98
- }, [itemWidth, throttle, columnGap]);
82
+ task();
83
+ }, []);
84
+ (0, import_react.useEffect)(() => {
85
+ if (throttle === 0)
86
+ return task();
87
+ const timeout = setTimeout(task, throttle || 200);
88
+ return () => clearTimeout(timeout);
89
+ }, [width, itemWidth, throttle, columnGap]);
99
90
  (0, import_react.useEffect)(() => {
100
91
  onSizeChange?.({ width, height, itemWidth, itemHeight, columnGap: columnGapSize, columnCount, rowGap, rowCount: contentShownRows, overflow: data.length > contentShownRows * columnCount, itemCount: data.length, maxRows: maxRows ?? null });
101
92
  }, [width, height, columnGapSize, columnCount, rowGap, contentShownRows, data.length, itemWidth, itemHeight, maxRows]);
@@ -114,20 +105,20 @@ function Flow(props) {
114
105
  style: (0, import_utils.transformCSSVariable)({ height: (0, import_utils.px)(height) }, style),
115
106
  ...rest
116
107
  },
117
- showItems && data.map((item, index, arr) => /* @__PURE__ */ React.createElement(
108
+ data.map((item, index, arr) => /* @__PURE__ */ React.createElement(
118
109
  "div",
119
110
  {
120
111
  key: keyExactor ? keyExactor(item, index, arr) : index,
121
112
  className: (0, import_deepsea_tools.clsx)(
122
113
  import_css.css`
123
- position: absolute;
124
- width: var(--width);
125
- height: var(--height);
126
- transition: var(--transition);
127
- left: 0;
128
- top: 0;
129
- transform: translate(var(--left), var(--top));
130
- `,
114
+ position: absolute;
115
+ width: var(--width);
116
+ height: var(--height);
117
+ transition: var(--transition);
118
+ left: 0;
119
+ top: 0;
120
+ transform: translate(var(--left), var(--top));
121
+ `,
131
122
  wrapperClassName
132
123
  ),
133
124
  style: (0, import_utils.transformCSSVariable)(
@@ -146,10 +137,10 @@ function Flow(props) {
146
137
  {
147
138
  className: (0, import_deepsea_tools.clsx)(
148
139
  import_css.css`
149
- width: 100%;
150
- height: 100%;
151
- display: var(--display);
152
- `,
140
+ width: 100%;
141
+ height: 100%;
142
+ display: var(--display);
143
+ `,
153
144
  containerClassName
154
145
  ),
155
146
  style: (0, import_utils.transformCSSVariable)({ display: getHidden(index) ? "none" : "block" }, containerStyle)
@@ -159,6 +150,13 @@ function Flow(props) {
159
150
  ))
160
151
  );
161
152
  }
153
+ function Flow(props) {
154
+ const ele = (0, import_react.useRef)(null);
155
+ const size = (0, import_ahooks.useSize)(ele);
156
+ if (!size)
157
+ return /* @__PURE__ */ React.createElement("div", { ref: ele, ...props });
158
+ return /* @__PURE__ */ React.createElement(RealFlow, { element: ele, ...props, width: size.width });
159
+ }
162
160
  // Annotate the CommonJS export names for ESM import in node:
163
161
  0 && (module.exports = {
164
162
  Flow,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/components/Flow.tsx"],
4
- "sourcesContent": ["\"use client\"\r\n\r\nimport { css } from \"@emotion/css\"\r\nimport { clsx } from \"deepsea-tools\"\r\nimport { CSSProperties, HTMLAttributes, Key, ReactNode, useEffect, useRef, useState } from \"react\"\r\nimport { px, transformCSSVariable } from \"../utils\"\r\n\r\nexport interface FlowSizeData {\r\n /** 容器宽度 */\r\n width: number\r\n /** 容器高度 */\r\n height: number\r\n /** 元素宽度 */\r\n itemWidth: number\r\n /** 元素高度 */\r\n itemHeight: number\r\n /** 列间距 */\r\n columnGap: number\r\n /** 列数 */\r\n columnCount: number\r\n /** 行间距 */\r\n rowGap: number\r\n /** 行数 */\r\n rowCount: number\r\n /** 元素格数 */\r\n itemCount: number\r\n /** 最大行数 */\r\n maxRows: number | null\r\n /** 是否有元素被隐藏 */\r\n overflow: boolean\r\n}\r\n\r\nexport interface FlowProps<T> extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\r\n /** 元素宽度 */\r\n itemWidth: number\r\n /** 元素高度 */\r\n itemHeight: number\r\n /**\r\n * 列间距\r\n * 1. 如果是数字,表示列间距是固定的\r\n * 2. 如果是 auto,表示会在尽可能放进更多列的情况下,列间距是平均的\r\n * 2. 如果是数组,表示列间距是区间的,第一个元素是最小值,第二个元素是最大值\r\n */\r\n columnGap?: number | \"auto\" | [number | \"auto\", number | \"auto\"]\r\n /** 行间距 */\r\n rowGap?: number\r\n gap?: number\r\n /** 最大行数 */\r\n maxRows?: number | null\r\n /** 源数据 */\r\n data: T[]\r\n /** 渲染 */\r\n render: (item: T, index: number, arr: T[]) => ReactNode\r\n /** key释放器,默认为 index */\r\n keyExactor?: (item: T, index: number, arr: T[]) => Key\r\n /**\r\n * 渲染的元素由两层容器包裹,外层容器类名\r\n */\r\n wrapperClassName?: string\r\n /**\r\n * 渲染的元素由两层容器包裹,外层容器样式\r\n */\r\n wrapperStyle?: CSSProperties\r\n /** \r\n * 渲染的元素由两层容器包裹,内层容器类名\r\n */\r\n containerClassName?: string\r\n /**\r\n * 渲染的元素由两层容器包裹,内层容器样式\r\n */\r\n containerStyle?: CSSProperties\r\n /** 节流时间,单位毫秒,默认200ms,传入 0 不节流 */\r\n throttle?: number\r\n /** 动画时间,单位毫秒,默认400ms,传入 0 不展示动画 */\r\n transitionDuration?: number\r\n /** 变化的回调函数 */\r\n onSizeChange?: (sizeData: FlowSizeData) => void\r\n}\r\n\r\nexport function getGapRange(gap?: undefined | number | \"auto\" | (number | \"auto\")[]): [number, number | \"auto\"] {\r\n if (typeof gap === \"number\") return [gap, gap]\r\n if (Array.isArray(gap)) return [typeof gap[0] === \"number\" ? gap[0] : 0, gap[1]]\r\n return [0, \"auto\"]\r\n}\r\n\r\nexport function getGapCountAndSize(width: number, itemWidth: number, minGap: number, maxGap: number | \"auto\"): [number, number] {\r\n const count = Math.floor((width + minGap) / (itemWidth + minGap)) || 1\r\n if (count === 1) return [count, 0]\r\n const averageGap = (width - itemWidth * count) / (count - 1)\r\n if (averageGap < minGap) return [count, minGap]\r\n if (maxGap !== \"auto\" && averageGap > maxGap) return [count, maxGap]\r\n return [count, averageGap]\r\n}\r\n\r\ninterface Position {\r\n left: number\r\n top: number\r\n}\r\n\r\n/** 自适应浮动组件 */\r\nexport function Flow<T>(props: FlowProps<T>) {\r\n let { itemWidth, itemHeight, columnGap, rowGap, maxRows, data, render, keyExactor, className, style, wrapperClassName, wrapperStyle, throttle, transitionDuration, onSizeChange, containerClassName, containerStyle, gap = 0, ...rest } = props\r\n rowGap ??= gap\r\n columnGap ??= gap\r\n const [minColumnGap, maxColumnGap] = getGapRange(columnGap)\r\n const [width, setWidth] = useState(0)\r\n const [columnCount, setColumnCount] = useState(1)\r\n const [columnGapSize, setColumnGapSize] = useState(minColumnGap)\r\n const [showItems, setShowItems] = useState(false)\r\n const ele = useRef<HTMLDivElement>(null)\r\n const contentRows = Math.ceil(data.length / columnCount)\r\n const contentShownRows = typeof maxRows === \"number\" ? Math.min(contentRows, maxRows) : contentRows\r\n const height = contentShownRows > 0 ? contentShownRows * (itemHeight + rowGap) - rowGap : 0\r\n\r\n function getPosition(index: number): Position {\r\n const y = Math.floor(index / columnCount)\r\n const x = index - y * columnCount\r\n return {\r\n left: x * (itemWidth + columnGapSize),\r\n top: y * (itemHeight + rowGap!)\r\n }\r\n }\r\n\r\n function getHidden(index: number) {\r\n if (typeof maxRows !== \"number\") return false\r\n return index >= maxRows * columnCount\r\n }\r\n\r\n useEffect(() => {\r\n let timeout: number\r\n const observer = new ResizeObserver(entries => {\r\n clearTimeout(timeout)\r\n function task() {\r\n const { width } = entries[0].contentRect\r\n const [newColumnCount, newColumnGapSize] = getGapCountAndSize(width, itemWidth, minColumnGap, maxColumnGap)\r\n setShowItems(true)\r\n setWidth(width)\r\n setColumnCount(newColumnCount)\r\n setColumnGapSize(newColumnGapSize)\r\n }\r\n if (throttle === 0) {\r\n task()\r\n } else {\r\n timeout = window.setTimeout(task, throttle || 200)\r\n }\r\n })\r\n observer.observe(ele.current!)\r\n\r\n return () => {\r\n observer.disconnect()\r\n }\r\n }, [itemWidth, throttle, columnGap])\r\n\r\n useEffect(() => {\r\n onSizeChange?.({ width, height, itemWidth, itemHeight, columnGap: columnGapSize, columnCount, rowGap: rowGap!, rowCount: contentShownRows, overflow: data.length > contentShownRows * columnCount, itemCount: data.length, maxRows: maxRows ?? null })\r\n }, [width, height, columnGapSize, columnCount, rowGap, contentShownRows, data.length, itemWidth, itemHeight, maxRows])\r\n\r\n return (\r\n <div\r\n ref={ele}\r\n className={clsx(\r\n css`\r\n position: relative;\r\n height: var(--height);\r\n overflow: hidden;\r\n `,\r\n className\r\n )}\r\n style={transformCSSVariable({ height: px(height) }, style)}\r\n {...rest}>\r\n {showItems &&\r\n data.map((item, index, arr) => (\r\n <div\r\n key={keyExactor ? keyExactor(item, index, arr) : index}\r\n className={clsx(\r\n css`\r\n position: absolute;\r\n width: var(--width);\r\n height: var(--height);\r\n transition: var(--transition);\r\n left: 0;\r\n top: 0;\r\n transform: translate(var(--left), var(--top));\r\n `,\r\n wrapperClassName\r\n )}\r\n style={transformCSSVariable(\r\n {\r\n width: px(itemWidth),\r\n height: px(itemHeight),\r\n transition: transitionDuration === 0 ? \"none\" : `all ${transitionDuration || 400}ms`,\r\n left: px(getPosition(index).left),\r\n top: px(getPosition(index).top)\r\n },\r\n wrapperStyle\r\n )}>\r\n <div\r\n className={clsx(\r\n css`\r\n width: 100%;\r\n height: 100%;\r\n display: var(--display);\r\n `,\r\n containerClassName\r\n )}\r\n style={transformCSSVariable({ display: getHidden(index) ? \"none\" : \"block\" }, containerStyle)}>\r\n {render(item, index, arr)}\r\n </div>\r\n </div>\r\n ))}\r\n </div>\r\n )\r\n}\r\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,iBAAoB;AACpB,2BAAqB;AACrB,mBAA2F;AAC3F,mBAAyC;AA0ElC,SAAS,YAAY,KAAoF;AAC5G,MAAI,OAAO,QAAQ;AAAU,WAAO,CAAC,KAAK,GAAG;AAC7C,MAAI,MAAM,QAAQ,GAAG;AAAG,WAAO,CAAC,OAAO,IAAI,CAAC,MAAM,WAAW,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;AAC/E,SAAO,CAAC,GAAG,MAAM;AACrB;AAEO,SAAS,mBAAmB,OAAe,WAAmB,QAAgB,QAA2C;AAC5H,QAAM,QAAQ,KAAK,OAAO,QAAQ,WAAW,YAAY,OAAO,KAAK;AACrE,MAAI,UAAU;AAAG,WAAO,CAAC,OAAO,CAAC;AACjC,QAAM,cAAc,QAAQ,YAAY,UAAU,QAAQ;AAC1D,MAAI,aAAa;AAAQ,WAAO,CAAC,OAAO,MAAM;AAC9C,MAAI,WAAW,UAAU,aAAa;AAAQ,WAAO,CAAC,OAAO,MAAM;AACnE,SAAO,CAAC,OAAO,UAAU;AAC7B;AAQO,SAAS,KAAQ,OAAqB;AACzC,MAAI,EAAE,WAAW,YAAY,WAAW,QAAQ,SAAS,MAAM,QAAQ,YAAY,WAAW,OAAO,kBAAkB,cAAc,UAAU,oBAAoB,cAAc,oBAAoB,gBAAgB,MAAM,GAAG,GAAG,KAAK,IAAI;AAC1O,aAAW;AACX,gBAAc;AACd,QAAM,CAAC,cAAc,YAAY,IAAI,YAAY,SAAS;AAC1D,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAS,CAAC;AACpC,QAAM,CAAC,aAAa,cAAc,QAAI,uBAAS,CAAC;AAChD,QAAM,CAAC,eAAe,gBAAgB,QAAI,uBAAS,YAAY;AAC/D,QAAM,CAAC,WAAW,YAAY,QAAI,uBAAS,KAAK;AAChD,QAAM,UAAM,qBAAuB,IAAI;AACvC,QAAM,cAAc,KAAK,KAAK,KAAK,SAAS,WAAW;AACvD,QAAM,mBAAmB,OAAO,YAAY,WAAW,KAAK,IAAI,aAAa,OAAO,IAAI;AACxF,QAAM,SAAS,mBAAmB,IAAI,oBAAoB,aAAa,UAAU,SAAS;AAE1F,WAAS,YAAY,OAAyB;AAC1C,UAAM,IAAI,KAAK,MAAM,QAAQ,WAAW;AACxC,UAAM,IAAI,QAAQ,IAAI;AACtB,WAAO;AAAA,MACH,MAAM,KAAK,YAAY;AAAA,MACvB,KAAK,KAAK,aAAa;AAAA,IAC3B;AAAA,EACJ;AAEA,WAAS,UAAU,OAAe;AAC9B,QAAI,OAAO,YAAY;AAAU,aAAO;AACxC,WAAO,SAAS,UAAU;AAAA,EAC9B;AAEA,8BAAU,MAAM;AACZ,QAAI;AACJ,UAAM,WAAW,IAAI,eAAe,aAAW;AAC3C,mBAAa,OAAO;AACpB,eAAS,OAAO;AACZ,cAAM,EAAE,OAAAA,OAAM,IAAI,QAAQ,CAAC,EAAE;AAC7B,cAAM,CAAC,gBAAgB,gBAAgB,IAAI,mBAAmBA,QAAO,WAAW,cAAc,YAAY;AAC1G,qBAAa,IAAI;AACjB,iBAASA,MAAK;AACd,uBAAe,cAAc;AAC7B,yBAAiB,gBAAgB;AAAA,MACrC;AACA,UAAI,aAAa,GAAG;AAChB,aAAK;AAAA,MACT,OAAO;AACH,kBAAU,OAAO,WAAW,MAAM,YAAY,GAAG;AAAA,MACrD;AAAA,IACJ,CAAC;AACD,aAAS,QAAQ,IAAI,OAAQ;AAE7B,WAAO,MAAM;AACT,eAAS,WAAW;AAAA,IACxB;AAAA,EACJ,GAAG,CAAC,WAAW,UAAU,SAAS,CAAC;AAEnC,8BAAU,MAAM;AACZ,mBAAe,EAAE,OAAO,QAAQ,WAAW,YAAY,WAAW,eAAe,aAAa,QAAiB,UAAU,kBAAkB,UAAU,KAAK,SAAS,mBAAmB,aAAa,WAAW,KAAK,QAAQ,SAAS,WAAW,KAAK,CAAC;AAAA,EACzP,GAAG,CAAC,OAAO,QAAQ,eAAe,aAAa,QAAQ,kBAAkB,KAAK,QAAQ,WAAW,YAAY,OAAO,CAAC;AAErH,SACI;AAAA,IAAC;AAAA;AAAA,MACG,KAAK;AAAA,MACL,eAAW;AAAA,QACP;AAAA;AAAA;AAAA;AAAA;AAAA,QAKA;AAAA,MACJ;AAAA,MACA,WAAO,mCAAqB,EAAE,YAAQ,iBAAG,MAAM,EAAE,GAAG,KAAK;AAAA,MACxD,GAAG;AAAA;AAAA,IACH,aACG,KAAK,IAAI,CAAC,MAAM,OAAO,QACnB;AAAA,MAAC;AAAA;AAAA,QACG,KAAK,aAAa,WAAW,MAAM,OAAO,GAAG,IAAI;AAAA,QACjD,eAAW;AAAA,UACP;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UASA;AAAA,QACJ;AAAA,QACA,WAAO;AAAA,UACH;AAAA,YACI,WAAO,iBAAG,SAAS;AAAA,YACnB,YAAQ,iBAAG,UAAU;AAAA,YACrB,YAAY,uBAAuB,IAAI,SAAS,OAAO,sBAAsB;AAAA,YAC7E,UAAM,iBAAG,YAAY,KAAK,EAAE,IAAI;AAAA,YAChC,SAAK,iBAAG,YAAY,KAAK,EAAE,GAAG;AAAA,UAClC;AAAA,UACA;AAAA,QACJ;AAAA;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACG,eAAW;AAAA,YACP;AAAA;AAAA;AAAA;AAAA;AAAA,YAKA;AAAA,UACJ;AAAA,UACA,WAAO,mCAAqB,EAAE,SAAS,UAAU,KAAK,IAAI,SAAS,QAAQ,GAAG,cAAc;AAAA;AAAA,QAC3F,OAAO,MAAM,OAAO,GAAG;AAAA,MAC5B;AAAA,IACJ,CACH;AAAA,EACT;AAER;",
6
- "names": ["width"]
4
+ "sourcesContent": ["\"use client\"\r\n\r\nimport { css } from \"@emotion/css\"\r\nimport { useSize } from \"ahooks\"\r\nimport { clsx } from \"deepsea-tools\"\r\nimport { CSSProperties, HTMLAttributes, Key, ReactNode, RefObject, useEffect, useImperativeHandle, useRef, useState } from \"react\"\r\nimport { px, transformCSSVariable } from \"../utils\"\r\n\r\nexport interface FlowSizeData {\r\n /** 容器宽度 */\r\n width: number\r\n /** 容器高度 */\r\n height: number\r\n /** 元素宽度 */\r\n itemWidth: number\r\n /** 元素高度 */\r\n itemHeight: number\r\n /** 列间距 */\r\n columnGap: number\r\n /** 列数 */\r\n columnCount: number\r\n /** 行间距 */\r\n rowGap: number\r\n /** 行数 */\r\n rowCount: number\r\n /** 元素格数 */\r\n itemCount: number\r\n /** 最大行数 */\r\n maxRows: number | null\r\n /** 是否有元素被隐藏 */\r\n overflow: boolean\r\n}\r\n\r\nexport interface FlowProps<T> extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\r\n /** 元素宽度 */\r\n itemWidth: number\r\n /** 元素高度 */\r\n itemHeight: number\r\n /**\r\n * 列间距\r\n * 1. 如果是数字,表示列间距是固定的\r\n * 2. 如果是 auto,表示会在尽可能放进更多列的情况下,列间距是平均的\r\n * 2. 如果是数组,表示列间距是区间的,第一个元素是最小值,第二个元素是最大值\r\n */\r\n columnGap?: number | \"auto\" | [number | \"auto\", number | \"auto\"]\r\n /** 行间距 */\r\n rowGap?: number\r\n gap?: number\r\n /** 最大行数 */\r\n maxRows?: number | null\r\n /** 源数据 */\r\n data: T[]\r\n /** 渲染 */\r\n render: (item: T, index: number, arr: T[]) => ReactNode\r\n /** key释放器,默认为 index */\r\n keyExactor?: (item: T, index: number, arr: T[]) => Key\r\n /**\r\n * 渲染的元素由两层容器包裹,外层容器类名\r\n */\r\n wrapperClassName?: string\r\n /**\r\n * 渲染的元素由两层容器包裹,外层容器样式\r\n */\r\n wrapperStyle?: CSSProperties\r\n /**\r\n * 渲染的元素由两层容器包裹,内层容器类名\r\n */\r\n containerClassName?: string\r\n /**\r\n * 渲染的元素由两层容器包裹,内层容器样式\r\n */\r\n containerStyle?: CSSProperties\r\n /** 节流时间,单位毫秒,默认200ms,传入 0 不节流 */\r\n throttle?: number\r\n /** 动画时间,单位毫秒,默认400ms,传入 0 不展示动画 */\r\n transitionDuration?: number\r\n /** 变化的回调函数 */\r\n onSizeChange?: (sizeData: FlowSizeData) => void\r\n element?: RefObject<HTMLDivElement>\r\n}\r\n\r\nexport function getGapRange(gap?: undefined | number | \"auto\" | (number | \"auto\")[]): [number, number | \"auto\"] {\r\n if (typeof gap === \"number\") return [gap, gap]\r\n if (Array.isArray(gap)) return [typeof gap[0] === \"number\" ? gap[0] : 0, gap[1]]\r\n return [0, \"auto\"]\r\n}\r\n\r\nexport function getGapCountAndSize(width: number, itemWidth: number, minGap: number, maxGap: number | \"auto\"): [number, number] {\r\n const count = Math.floor((width + minGap) / (itemWidth + minGap)) || 1\r\n if (count === 1) return [count, 0]\r\n const averageGap = (width - itemWidth * count) / (count - 1)\r\n if (averageGap < minGap) return [count, minGap]\r\n if (maxGap !== \"auto\" && averageGap > maxGap) return [count, maxGap]\r\n return [count, averageGap]\r\n}\r\n\r\ninterface Position {\r\n left: number\r\n top: number\r\n}\r\n\r\nfunction RealFlow<T>(props: FlowProps<T> & { width: number }) {\r\n let { itemWidth, itemHeight, columnGap, rowGap, maxRows, data, render, keyExactor, className, style, wrapperClassName, wrapperStyle, throttle, transitionDuration, onSizeChange, containerClassName, containerStyle, gap = 0, width, element, ...rest } = props\r\n rowGap ??= gap\r\n columnGap ??= gap\r\n const [minColumnGap, maxColumnGap] = getGapRange(columnGap)\r\n const [columnCount, setColumnCount] = useState(1)\r\n const [columnGapSize, setColumnGapSize] = useState(minColumnGap)\r\n\r\n const ele = useRef<HTMLDivElement>(null)\r\n const contentRows = Math.ceil(data.length / columnCount)\r\n const contentShownRows = typeof maxRows === \"number\" ? Math.min(contentRows, maxRows) : contentRows\r\n const height = contentShownRows > 0 ? contentShownRows * (itemHeight + rowGap) - rowGap : 0\r\n\r\n function getPosition(index: number): Position {\r\n const y = Math.floor(index / columnCount)\r\n const x = index - y * columnCount\r\n return {\r\n left: x * (itemWidth + columnGapSize),\r\n top: y * (itemHeight + rowGap!)\r\n }\r\n }\r\n\r\n function getHidden(index: number) {\r\n if (typeof maxRows !== \"number\") return false\r\n return index >= maxRows * columnCount\r\n }\r\n\r\n function task() {\r\n const [newColumnCount, newColumnGapSize] = getGapCountAndSize(width, itemWidth, minColumnGap, maxColumnGap)\r\n setColumnCount(newColumnCount)\r\n setColumnGapSize(newColumnGapSize)\r\n }\r\n\r\n useImperativeHandle(element, () => ele.current!, [ele.current])\r\n\r\n useEffect(() => {\r\n task()\r\n }, [])\r\n\r\n useEffect(() => {\r\n if (throttle === 0) return task()\r\n const timeout = setTimeout(task, throttle || 200)\r\n return () => clearTimeout(timeout)\r\n }, [width, itemWidth, throttle, columnGap])\r\n\r\n useEffect(() => {\r\n onSizeChange?.({ width, height, itemWidth, itemHeight, columnGap: columnGapSize, columnCount, rowGap: rowGap!, rowCount: contentShownRows, overflow: data.length > contentShownRows * columnCount, itemCount: data.length, maxRows: maxRows ?? null })\r\n }, [width, height, columnGapSize, columnCount, rowGap, contentShownRows, data.length, itemWidth, itemHeight, maxRows])\r\n\r\n return (\r\n <div\r\n ref={ele}\r\n className={clsx(\r\n css`\r\n position: relative;\r\n height: var(--height);\r\n overflow: hidden;\r\n `,\r\n className\r\n )}\r\n style={transformCSSVariable({ height: px(height) }, style)}\r\n {...rest}>\r\n {data.map((item, index, arr) => (\r\n <div\r\n key={keyExactor ? keyExactor(item, index, arr) : index}\r\n className={clsx(\r\n css`\r\n position: absolute;\r\n width: var(--width);\r\n height: var(--height);\r\n transition: var(--transition);\r\n left: 0;\r\n top: 0;\r\n transform: translate(var(--left), var(--top));\r\n `,\r\n wrapperClassName\r\n )}\r\n style={transformCSSVariable(\r\n {\r\n width: px(itemWidth),\r\n height: px(itemHeight),\r\n transition: transitionDuration === 0 ? \"none\" : `all ${transitionDuration || 400}ms`,\r\n left: px(getPosition(index).left),\r\n top: px(getPosition(index).top)\r\n },\r\n wrapperStyle\r\n )}>\r\n <div\r\n className={clsx(\r\n css`\r\n width: 100%;\r\n height: 100%;\r\n display: var(--display);\r\n `,\r\n containerClassName\r\n )}\r\n style={transformCSSVariable({ display: getHidden(index) ? \"none\" : \"block\" }, containerStyle)}>\r\n {render(item, index, arr)}\r\n </div>\r\n </div>\r\n ))}\r\n </div>\r\n )\r\n}\r\n\r\n/** 自适应浮动组件 */\r\nexport function Flow<T>(props: FlowProps<T>) {\r\n const ele = useRef<HTMLDivElement>(null)\r\n const size = useSize(ele)\r\n if (!size) return <div ref={ele} {...props} />\r\n return <RealFlow element={ele} {...props} width={size.width} />\r\n}\r\n"],
5
+ "mappings": "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",
6
+ "names": []
7
7
  }
@@ -1,4 +1,4 @@
1
- import { CSSProperties, HTMLAttributes, Key, ReactNode } from "react";
1
+ import { CSSProperties, HTMLAttributes, Key, ReactNode, RefObject } from "react";
2
2
  export interface FlowSizeData {
3
3
  /** 容器宽度 */
4
4
  width: number;
@@ -68,6 +68,7 @@ export interface FlowProps<T> extends Omit<HTMLAttributes<HTMLDivElement>, "chil
68
68
  transitionDuration?: number;
69
69
  /** 变化的回调函数 */
70
70
  onSizeChange?: (sizeData: FlowSizeData) => void;
71
+ element?: RefObject<HTMLDivElement>;
71
72
  }
72
73
  export declare function getGapRange(gap?: undefined | number | "auto" | (number | "auto")[]): [number, number | "auto"];
73
74
  export declare function getGapCountAndSize(width: number, itemWidth: number, minGap: number, maxGap: number | "auto"): [number, number];
@@ -1,8 +1,9 @@
1
1
  "use client";
2
2
 
3
3
  import { css } from "@emotion/css";
4
+ import { useSize } from "ahooks";
4
5
  import { clsx } from "deepsea-tools";
5
- import { useEffect, useRef, useState } from "react";
6
+ import { useEffect, useImperativeHandle, useRef, useState } from "react";
6
7
  import { px, transformCSSVariable } from "../utils";
7
8
  import { jsx as _jsx } from "react/jsx-runtime";
8
9
  export function getGapRange(gap) {
@@ -18,8 +19,7 @@ export function getGapCountAndSize(width, itemWidth, minGap, maxGap) {
18
19
  if (maxGap !== "auto" && averageGap > maxGap) return [count, maxGap];
19
20
  return [count, averageGap];
20
21
  }
21
- /** 自适应浮动组件 */
22
- export function Flow(props) {
22
+ function RealFlow(props) {
23
23
  let {
24
24
  itemWidth,
25
25
  itemHeight,
@@ -39,15 +39,15 @@ export function Flow(props) {
39
39
  containerClassName,
40
40
  containerStyle,
41
41
  gap = 0,
42
+ width,
43
+ element,
42
44
  ...rest
43
45
  } = props;
44
46
  rowGap ??= gap;
45
47
  columnGap ??= gap;
46
48
  const [minColumnGap, maxColumnGap] = getGapRange(columnGap);
47
- const [width, setWidth] = useState(0);
48
49
  const [columnCount, setColumnCount] = useState(1);
49
50
  const [columnGapSize, setColumnGapSize] = useState(minColumnGap);
50
- const [showItems, setShowItems] = useState(false);
51
51
  const ele = useRef(null);
52
52
  const contentRows = Math.ceil(data.length / columnCount);
53
53
  const contentShownRows = typeof maxRows === "number" ? Math.min(contentRows, maxRows) : contentRows;
@@ -64,31 +64,20 @@ export function Flow(props) {
64
64
  if (typeof maxRows !== "number") return false;
65
65
  return index >= maxRows * columnCount;
66
66
  }
67
+ function task() {
68
+ const [newColumnCount, newColumnGapSize] = getGapCountAndSize(width, itemWidth, minColumnGap, maxColumnGap);
69
+ setColumnCount(newColumnCount);
70
+ setColumnGapSize(newColumnGapSize);
71
+ }
72
+ useImperativeHandle(element, () => ele.current, [ele.current]);
67
73
  useEffect(() => {
68
- let timeout;
69
- const observer = new ResizeObserver(entries => {
70
- clearTimeout(timeout);
71
- function task() {
72
- const {
73
- width
74
- } = entries[0].contentRect;
75
- const [newColumnCount, newColumnGapSize] = getGapCountAndSize(width, itemWidth, minColumnGap, maxColumnGap);
76
- setShowItems(true);
77
- setWidth(width);
78
- setColumnCount(newColumnCount);
79
- setColumnGapSize(newColumnGapSize);
80
- }
81
- if (throttle === 0) {
82
- task();
83
- } else {
84
- timeout = window.setTimeout(task, throttle || 200);
85
- }
86
- });
87
- observer.observe(ele.current);
88
- return () => {
89
- observer.disconnect();
90
- };
91
- }, [itemWidth, throttle, columnGap]);
74
+ task();
75
+ }, []);
76
+ useEffect(() => {
77
+ if (throttle === 0) return task();
78
+ const timeout = setTimeout(task, throttle || 200);
79
+ return () => clearTimeout(timeout);
80
+ }, [width, itemWidth, throttle, columnGap]);
92
81
  useEffect(() => {
93
82
  onSizeChange?.({
94
83
  width,
@@ -115,16 +104,16 @@ export function Flow(props) {
115
104
  height: px(height)
116
105
  }, style),
117
106
  ...rest,
118
- children: showItems && data.map((item, index, arr) => /*#__PURE__*/_jsx("div", {
107
+ children: data.map((item, index, arr) => /*#__PURE__*/_jsx("div", {
119
108
  className: clsx(css`
120
- position: absolute;
121
- width: var(--width);
122
- height: var(--height);
123
- transition: var(--transition);
124
- left: 0;
125
- top: 0;
126
- transform: translate(var(--left), var(--top));
127
- `, wrapperClassName),
109
+ position: absolute;
110
+ width: var(--width);
111
+ height: var(--height);
112
+ transition: var(--transition);
113
+ left: 0;
114
+ top: 0;
115
+ transform: translate(var(--left), var(--top));
116
+ `, wrapperClassName),
128
117
  style: transformCSSVariable({
129
118
  width: px(itemWidth),
130
119
  height: px(itemHeight),
@@ -134,10 +123,10 @@ export function Flow(props) {
134
123
  }, wrapperStyle),
135
124
  children: /*#__PURE__*/_jsx("div", {
136
125
  className: clsx(css`
137
- width: 100%;
138
- height: 100%;
139
- display: var(--display);
140
- `, containerClassName),
126
+ width: 100%;
127
+ height: 100%;
128
+ display: var(--display);
129
+ `, containerClassName),
141
130
  style: transformCSSVariable({
142
131
  display: getHidden(index) ? "none" : "block"
143
132
  }, containerStyle),
@@ -146,4 +135,19 @@ export function Flow(props) {
146
135
  }, keyExactor ? keyExactor(item, index, arr) : index))
147
136
  });
148
137
  }
138
+
139
+ /** 自适应浮动组件 */
140
+ export function Flow(props) {
141
+ const ele = useRef(null);
142
+ const size = useSize(ele);
143
+ if (!size) return /*#__PURE__*/_jsx("div", {
144
+ ref: ele,
145
+ ...props
146
+ });
147
+ return /*#__PURE__*/_jsx(RealFlow, {
148
+ element: ele,
149
+ ...props,
150
+ width: size.width
151
+ });
152
+ }
149
153
  //# sourceMappingURL=Flow.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["css","clsx","useEffect","useRef","useState","px","transformCSSVariable","jsx","_jsx","getGapRange","gap","Array","isArray","getGapCountAndSize","width","itemWidth","minGap","maxGap","count","Math","floor","averageGap","Flow","props","itemHeight","columnGap","rowGap","maxRows","data","render","keyExactor","className","style","wrapperClassName","wrapperStyle","throttle","transitionDuration","onSizeChange","containerClassName","containerStyle","rest","minColumnGap","maxColumnGap","setWidth","columnCount","setColumnCount","columnGapSize","setColumnGapSize","showItems","setShowItems","ele","contentRows","ceil","length","contentShownRows","min","height","getPosition","index","y","x","left","top","getHidden","timeout","observer","ResizeObserver","entries","clearTimeout","task","contentRect","newColumnCount","newColumnGapSize","window","setTimeout","observe","current","disconnect","rowCount","overflow","itemCount","ref","children","map","item","arr","transition","display"],"sources":["../../../src/components/Flow.tsx"],"sourcesContent":["\"use client\"\r\n\r\nimport { css } from \"@emotion/css\"\r\nimport { clsx } from \"deepsea-tools\"\r\nimport { CSSProperties, HTMLAttributes, Key, ReactNode, useEffect, useRef, useState } from \"react\"\r\nimport { px, transformCSSVariable } from \"../utils\"\r\n\r\nexport interface FlowSizeData {\r\n /** 容器宽度 */\r\n width: number\r\n /** 容器高度 */\r\n height: number\r\n /** 元素宽度 */\r\n itemWidth: number\r\n /** 元素高度 */\r\n itemHeight: number\r\n /** 列间距 */\r\n columnGap: number\r\n /** 列数 */\r\n columnCount: number\r\n /** 行间距 */\r\n rowGap: number\r\n /** 行数 */\r\n rowCount: number\r\n /** 元素格数 */\r\n itemCount: number\r\n /** 最大行数 */\r\n maxRows: number | null\r\n /** 是否有元素被隐藏 */\r\n overflow: boolean\r\n}\r\n\r\nexport interface FlowProps<T> extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\r\n /** 元素宽度 */\r\n itemWidth: number\r\n /** 元素高度 */\r\n itemHeight: number\r\n /**\r\n * 列间距\r\n * 1. 如果是数字,表示列间距是固定的\r\n * 2. 如果是 auto,表示会在尽可能放进更多列的情况下,列间距是平均的\r\n * 2. 如果是数组,表示列间距是区间的,第一个元素是最小值,第二个元素是最大值\r\n */\r\n columnGap?: number | \"auto\" | [number | \"auto\", number | \"auto\"]\r\n /** 行间距 */\r\n rowGap?: number\r\n gap?: number\r\n /** 最大行数 */\r\n maxRows?: number | null\r\n /** 源数据 */\r\n data: T[]\r\n /** 渲染 */\r\n render: (item: T, index: number, arr: T[]) => ReactNode\r\n /** key释放器,默认为 index */\r\n keyExactor?: (item: T, index: number, arr: T[]) => Key\r\n /**\r\n * 渲染的元素由两层容器包裹,外层容器类名\r\n */\r\n wrapperClassName?: string\r\n /**\r\n * 渲染的元素由两层容器包裹,外层容器样式\r\n */\r\n wrapperStyle?: CSSProperties\r\n /** \r\n * 渲染的元素由两层容器包裹,内层容器类名\r\n */\r\n containerClassName?: string\r\n /**\r\n * 渲染的元素由两层容器包裹,内层容器样式\r\n */\r\n containerStyle?: CSSProperties\r\n /** 节流时间,单位毫秒,默认200ms,传入 0 不节流 */\r\n throttle?: number\r\n /** 动画时间,单位毫秒,默认400ms,传入 0 不展示动画 */\r\n transitionDuration?: number\r\n /** 变化的回调函数 */\r\n onSizeChange?: (sizeData: FlowSizeData) => void\r\n}\r\n\r\nexport function getGapRange(gap?: undefined | number | \"auto\" | (number | \"auto\")[]): [number, number | \"auto\"] {\r\n if (typeof gap === \"number\") return [gap, gap]\r\n if (Array.isArray(gap)) return [typeof gap[0] === \"number\" ? gap[0] : 0, gap[1]]\r\n return [0, \"auto\"]\r\n}\r\n\r\nexport function getGapCountAndSize(width: number, itemWidth: number, minGap: number, maxGap: number | \"auto\"): [number, number] {\r\n const count = Math.floor((width + minGap) / (itemWidth + minGap)) || 1\r\n if (count === 1) return [count, 0]\r\n const averageGap = (width - itemWidth * count) / (count - 1)\r\n if (averageGap < minGap) return [count, minGap]\r\n if (maxGap !== \"auto\" && averageGap > maxGap) return [count, maxGap]\r\n return [count, averageGap]\r\n}\r\n\r\ninterface Position {\r\n left: number\r\n top: number\r\n}\r\n\r\n/** 自适应浮动组件 */\r\nexport function Flow<T>(props: FlowProps<T>) {\r\n let { itemWidth, itemHeight, columnGap, rowGap, maxRows, data, render, keyExactor, className, style, wrapperClassName, wrapperStyle, throttle, transitionDuration, onSizeChange, containerClassName, containerStyle, gap = 0, ...rest } = props\r\n rowGap ??= gap\r\n columnGap ??= gap\r\n const [minColumnGap, maxColumnGap] = getGapRange(columnGap)\r\n const [width, setWidth] = useState(0)\r\n const [columnCount, setColumnCount] = useState(1)\r\n const [columnGapSize, setColumnGapSize] = useState(minColumnGap)\r\n const [showItems, setShowItems] = useState(false)\r\n const ele = useRef<HTMLDivElement>(null)\r\n const contentRows = Math.ceil(data.length / columnCount)\r\n const contentShownRows = typeof maxRows === \"number\" ? Math.min(contentRows, maxRows) : contentRows\r\n const height = contentShownRows > 0 ? contentShownRows * (itemHeight + rowGap) - rowGap : 0\r\n\r\n function getPosition(index: number): Position {\r\n const y = Math.floor(index / columnCount)\r\n const x = index - y * columnCount\r\n return {\r\n left: x * (itemWidth + columnGapSize),\r\n top: y * (itemHeight + rowGap!)\r\n }\r\n }\r\n\r\n function getHidden(index: number) {\r\n if (typeof maxRows !== \"number\") return false\r\n return index >= maxRows * columnCount\r\n }\r\n\r\n useEffect(() => {\r\n let timeout: number\r\n const observer = new ResizeObserver(entries => {\r\n clearTimeout(timeout)\r\n function task() {\r\n const { width } = entries[0].contentRect\r\n const [newColumnCount, newColumnGapSize] = getGapCountAndSize(width, itemWidth, minColumnGap, maxColumnGap)\r\n setShowItems(true)\r\n setWidth(width)\r\n setColumnCount(newColumnCount)\r\n setColumnGapSize(newColumnGapSize)\r\n }\r\n if (throttle === 0) {\r\n task()\r\n } else {\r\n timeout = window.setTimeout(task, throttle || 200)\r\n }\r\n })\r\n observer.observe(ele.current!)\r\n\r\n return () => {\r\n observer.disconnect()\r\n }\r\n }, [itemWidth, throttle, columnGap])\r\n\r\n useEffect(() => {\r\n onSizeChange?.({ width, height, itemWidth, itemHeight, columnGap: columnGapSize, columnCount, rowGap: rowGap!, rowCount: contentShownRows, overflow: data.length > contentShownRows * columnCount, itemCount: data.length, maxRows: maxRows ?? null })\r\n }, [width, height, columnGapSize, columnCount, rowGap, contentShownRows, data.length, itemWidth, itemHeight, maxRows])\r\n\r\n return (\r\n <div\r\n ref={ele}\r\n className={clsx(\r\n css`\r\n position: relative;\r\n height: var(--height);\r\n overflow: hidden;\r\n `,\r\n className\r\n )}\r\n style={transformCSSVariable({ height: px(height) }, style)}\r\n {...rest}>\r\n {showItems &&\r\n data.map((item, index, arr) => (\r\n <div\r\n key={keyExactor ? keyExactor(item, index, arr) : index}\r\n className={clsx(\r\n css`\r\n position: absolute;\r\n width: var(--width);\r\n height: var(--height);\r\n transition: var(--transition);\r\n left: 0;\r\n top: 0;\r\n transform: translate(var(--left), var(--top));\r\n `,\r\n wrapperClassName\r\n )}\r\n style={transformCSSVariable(\r\n {\r\n width: px(itemWidth),\r\n height: px(itemHeight),\r\n transition: transitionDuration === 0 ? \"none\" : `all ${transitionDuration || 400}ms`,\r\n left: px(getPosition(index).left),\r\n top: px(getPosition(index).top)\r\n },\r\n wrapperStyle\r\n )}>\r\n <div\r\n className={clsx(\r\n css`\r\n width: 100%;\r\n height: 100%;\r\n display: var(--display);\r\n `,\r\n containerClassName\r\n )}\r\n style={transformCSSVariable({ display: getHidden(index) ? \"none\" : \"block\" }, containerStyle)}>\r\n {render(item, index, arr)}\r\n </div>\r\n </div>\r\n ))}\r\n </div>\r\n )\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"names":["css","useSize","clsx","useEffect","useImperativeHandle","useRef","useState","px","transformCSSVariable","jsx","_jsx","getGapRange","gap","Array","isArray","getGapCountAndSize","width","itemWidth","minGap","maxGap","count","Math","floor","averageGap","RealFlow","props","itemHeight","columnGap","rowGap","maxRows","data","render","keyExactor","className","style","wrapperClassName","wrapperStyle","throttle","transitionDuration","onSizeChange","containerClassName","containerStyle","element","rest","minColumnGap","maxColumnGap","columnCount","setColumnCount","columnGapSize","setColumnGapSize","ele","contentRows","ceil","length","contentShownRows","min","height","getPosition","index","y","x","left","top","getHidden","task","newColumnCount","newColumnGapSize","current","timeout","setTimeout","clearTimeout","rowCount","overflow","itemCount","ref","children","map","item","arr","transition","display","Flow","size"],"sources":["../../../src/components/Flow.tsx"],"sourcesContent":["\"use client\"\r\n\r\nimport { css } from \"@emotion/css\"\r\nimport { useSize } from \"ahooks\"\r\nimport { clsx } from \"deepsea-tools\"\r\nimport { CSSProperties, HTMLAttributes, Key, ReactNode, RefObject, useEffect, useImperativeHandle, useRef, useState } from \"react\"\r\nimport { px, transformCSSVariable } from \"../utils\"\r\n\r\nexport interface FlowSizeData {\r\n /** 容器宽度 */\r\n width: number\r\n /** 容器高度 */\r\n height: number\r\n /** 元素宽度 */\r\n itemWidth: number\r\n /** 元素高度 */\r\n itemHeight: number\r\n /** 列间距 */\r\n columnGap: number\r\n /** 列数 */\r\n columnCount: number\r\n /** 行间距 */\r\n rowGap: number\r\n /** 行数 */\r\n rowCount: number\r\n /** 元素格数 */\r\n itemCount: number\r\n /** 最大行数 */\r\n maxRows: number | null\r\n /** 是否有元素被隐藏 */\r\n overflow: boolean\r\n}\r\n\r\nexport interface FlowProps<T> extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\r\n /** 元素宽度 */\r\n itemWidth: number\r\n /** 元素高度 */\r\n itemHeight: number\r\n /**\r\n * 列间距\r\n * 1. 如果是数字,表示列间距是固定的\r\n * 2. 如果是 auto,表示会在尽可能放进更多列的情况下,列间距是平均的\r\n * 2. 如果是数组,表示列间距是区间的,第一个元素是最小值,第二个元素是最大值\r\n */\r\n columnGap?: number | \"auto\" | [number | \"auto\", number | \"auto\"]\r\n /** 行间距 */\r\n rowGap?: number\r\n gap?: number\r\n /** 最大行数 */\r\n maxRows?: number | null\r\n /** 源数据 */\r\n data: T[]\r\n /** 渲染 */\r\n render: (item: T, index: number, arr: T[]) => ReactNode\r\n /** key释放器,默认为 index */\r\n keyExactor?: (item: T, index: number, arr: T[]) => Key\r\n /**\r\n * 渲染的元素由两层容器包裹,外层容器类名\r\n */\r\n wrapperClassName?: string\r\n /**\r\n * 渲染的元素由两层容器包裹,外层容器样式\r\n */\r\n wrapperStyle?: CSSProperties\r\n /**\r\n * 渲染的元素由两层容器包裹,内层容器类名\r\n */\r\n containerClassName?: string\r\n /**\r\n * 渲染的元素由两层容器包裹,内层容器样式\r\n */\r\n containerStyle?: CSSProperties\r\n /** 节流时间,单位毫秒,默认200ms,传入 0 不节流 */\r\n throttle?: number\r\n /** 动画时间,单位毫秒,默认400ms,传入 0 不展示动画 */\r\n transitionDuration?: number\r\n /** 变化的回调函数 */\r\n onSizeChange?: (sizeData: FlowSizeData) => void\r\n element?: RefObject<HTMLDivElement>\r\n}\r\n\r\nexport function getGapRange(gap?: undefined | number | \"auto\" | (number | \"auto\")[]): [number, number | \"auto\"] {\r\n if (typeof gap === \"number\") return [gap, gap]\r\n if (Array.isArray(gap)) return [typeof gap[0] === \"number\" ? gap[0] : 0, gap[1]]\r\n return [0, \"auto\"]\r\n}\r\n\r\nexport function getGapCountAndSize(width: number, itemWidth: number, minGap: number, maxGap: number | \"auto\"): [number, number] {\r\n const count = Math.floor((width + minGap) / (itemWidth + minGap)) || 1\r\n if (count === 1) return [count, 0]\r\n const averageGap = (width - itemWidth * count) / (count - 1)\r\n if (averageGap < minGap) return [count, minGap]\r\n if (maxGap !== \"auto\" && averageGap > maxGap) return [count, maxGap]\r\n return [count, averageGap]\r\n}\r\n\r\ninterface Position {\r\n left: number\r\n top: number\r\n}\r\n\r\nfunction RealFlow<T>(props: FlowProps<T> & { width: number }) {\r\n let { itemWidth, itemHeight, columnGap, rowGap, maxRows, data, render, keyExactor, className, style, wrapperClassName, wrapperStyle, throttle, transitionDuration, onSizeChange, containerClassName, containerStyle, gap = 0, width, element, ...rest } = props\r\n rowGap ??= gap\r\n columnGap ??= gap\r\n const [minColumnGap, maxColumnGap] = getGapRange(columnGap)\r\n const [columnCount, setColumnCount] = useState(1)\r\n const [columnGapSize, setColumnGapSize] = useState(minColumnGap)\r\n\r\n const ele = useRef<HTMLDivElement>(null)\r\n const contentRows = Math.ceil(data.length / columnCount)\r\n const contentShownRows = typeof maxRows === \"number\" ? Math.min(contentRows, maxRows) : contentRows\r\n const height = contentShownRows > 0 ? contentShownRows * (itemHeight + rowGap) - rowGap : 0\r\n\r\n function getPosition(index: number): Position {\r\n const y = Math.floor(index / columnCount)\r\n const x = index - y * columnCount\r\n return {\r\n left: x * (itemWidth + columnGapSize),\r\n top: y * (itemHeight + rowGap!)\r\n }\r\n }\r\n\r\n function getHidden(index: number) {\r\n if (typeof maxRows !== \"number\") return false\r\n return index >= maxRows * columnCount\r\n }\r\n\r\n function task() {\r\n const [newColumnCount, newColumnGapSize] = getGapCountAndSize(width, itemWidth, minColumnGap, maxColumnGap)\r\n setColumnCount(newColumnCount)\r\n setColumnGapSize(newColumnGapSize)\r\n }\r\n\r\n useImperativeHandle(element, () => ele.current!, [ele.current])\r\n\r\n useEffect(() => {\r\n task()\r\n }, [])\r\n\r\n useEffect(() => {\r\n if (throttle === 0) return task()\r\n const timeout = setTimeout(task, throttle || 200)\r\n return () => clearTimeout(timeout)\r\n }, [width, itemWidth, throttle, columnGap])\r\n\r\n useEffect(() => {\r\n onSizeChange?.({ width, height, itemWidth, itemHeight, columnGap: columnGapSize, columnCount, rowGap: rowGap!, rowCount: contentShownRows, overflow: data.length > contentShownRows * columnCount, itemCount: data.length, maxRows: maxRows ?? null })\r\n }, [width, height, columnGapSize, columnCount, rowGap, contentShownRows, data.length, itemWidth, itemHeight, maxRows])\r\n\r\n return (\r\n <div\r\n ref={ele}\r\n className={clsx(\r\n css`\r\n position: relative;\r\n height: var(--height);\r\n overflow: hidden;\r\n `,\r\n className\r\n )}\r\n style={transformCSSVariable({ height: px(height) }, style)}\r\n {...rest}>\r\n {data.map((item, index, arr) => (\r\n <div\r\n key={keyExactor ? keyExactor(item, index, arr) : index}\r\n className={clsx(\r\n css`\r\n position: absolute;\r\n width: var(--width);\r\n height: var(--height);\r\n transition: var(--transition);\r\n left: 0;\r\n top: 0;\r\n transform: translate(var(--left), var(--top));\r\n `,\r\n wrapperClassName\r\n )}\r\n style={transformCSSVariable(\r\n {\r\n width: px(itemWidth),\r\n height: px(itemHeight),\r\n transition: transitionDuration === 0 ? \"none\" : `all ${transitionDuration || 400}ms`,\r\n left: px(getPosition(index).left),\r\n top: px(getPosition(index).top)\r\n },\r\n wrapperStyle\r\n )}>\r\n <div\r\n className={clsx(\r\n css`\r\n width: 100%;\r\n height: 100%;\r\n display: var(--display);\r\n `,\r\n containerClassName\r\n )}\r\n style={transformCSSVariable({ display: getHidden(index) ? \"none\" : \"block\" }, containerStyle)}>\r\n {render(item, index, arr)}\r\n </div>\r\n </div>\r\n ))}\r\n </div>\r\n )\r\n}\r\n\r\n/** 自适应浮动组件 */\r\nexport function Flow<T>(props: FlowProps<T>) {\r\n const ele = useRef<HTMLDivElement>(null)\r\n const size = useSize(ele)\r\n if (!size) return <div ref={ele} {...props} />\r\n return <RealFlow element={ele} {...props} width={size.width} />\r\n}\r\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepsea-components",
3
- "version": "3.1.1",
3
+ "version": "3.2.0-beta.20240606184051",
4
4
  "description": "格数科技自用组件库",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -45,4 +45,4 @@
45
45
  "peerDependencies": {
46
46
  "react": "^18.3.1"
47
47
  }
48
- }
48
+ }
@@ -1,8 +1,9 @@
1
1
  "use client"
2
2
 
3
3
  import { css } from "@emotion/css"
4
+ import { useSize } from "ahooks"
4
5
  import { clsx } from "deepsea-tools"
5
- import { CSSProperties, HTMLAttributes, Key, ReactNode, useEffect, useRef, useState } from "react"
6
+ import { CSSProperties, HTMLAttributes, Key, ReactNode, RefObject, useEffect, useImperativeHandle, useRef, useState } from "react"
6
7
  import { px, transformCSSVariable } from "../utils"
7
8
 
8
9
  export interface FlowSizeData {
@@ -61,7 +62,7 @@ export interface FlowProps<T> extends Omit<HTMLAttributes<HTMLDivElement>, "chil
61
62
  * 渲染的元素由两层容器包裹,外层容器样式
62
63
  */
63
64
  wrapperStyle?: CSSProperties
64
- /**
65
+ /**
65
66
  * 渲染的元素由两层容器包裹,内层容器类名
66
67
  */
67
68
  containerClassName?: string
@@ -75,6 +76,7 @@ export interface FlowProps<T> extends Omit<HTMLAttributes<HTMLDivElement>, "chil
75
76
  transitionDuration?: number
76
77
  /** 变化的回调函数 */
77
78
  onSizeChange?: (sizeData: FlowSizeData) => void
79
+ element?: RefObject<HTMLDivElement>
78
80
  }
79
81
 
80
82
  export function getGapRange(gap?: undefined | number | "auto" | (number | "auto")[]): [number, number | "auto"] {
@@ -97,16 +99,14 @@ interface Position {
97
99
  top: number
98
100
  }
99
101
 
100
- /** 自适应浮动组件 */
101
- export function Flow<T>(props: FlowProps<T>) {
102
- let { itemWidth, itemHeight, columnGap, rowGap, maxRows, data, render, keyExactor, className, style, wrapperClassName, wrapperStyle, throttle, transitionDuration, onSizeChange, containerClassName, containerStyle, gap = 0, ...rest } = props
102
+ function RealFlow<T>(props: FlowProps<T> & { width: number }) {
103
+ let { itemWidth, itemHeight, columnGap, rowGap, maxRows, data, render, keyExactor, className, style, wrapperClassName, wrapperStyle, throttle, transitionDuration, onSizeChange, containerClassName, containerStyle, gap = 0, width, element, ...rest } = props
103
104
  rowGap ??= gap
104
105
  columnGap ??= gap
105
106
  const [minColumnGap, maxColumnGap] = getGapRange(columnGap)
106
- const [width, setWidth] = useState(0)
107
107
  const [columnCount, setColumnCount] = useState(1)
108
108
  const [columnGapSize, setColumnGapSize] = useState(minColumnGap)
109
- const [showItems, setShowItems] = useState(false)
109
+
110
110
  const ele = useRef<HTMLDivElement>(null)
111
111
  const contentRows = Math.ceil(data.length / columnCount)
112
112
  const contentShownRows = typeof maxRows === "number" ? Math.min(contentRows, maxRows) : contentRows
@@ -126,30 +126,23 @@ export function Flow<T>(props: FlowProps<T>) {
126
126
  return index >= maxRows * columnCount
127
127
  }
128
128
 
129
+ function task() {
130
+ const [newColumnCount, newColumnGapSize] = getGapCountAndSize(width, itemWidth, minColumnGap, maxColumnGap)
131
+ setColumnCount(newColumnCount)
132
+ setColumnGapSize(newColumnGapSize)
133
+ }
134
+
135
+ useImperativeHandle(element, () => ele.current!, [ele.current])
136
+
129
137
  useEffect(() => {
130
- let timeout: number
131
- const observer = new ResizeObserver(entries => {
132
- clearTimeout(timeout)
133
- function task() {
134
- const { width } = entries[0].contentRect
135
- const [newColumnCount, newColumnGapSize] = getGapCountAndSize(width, itemWidth, minColumnGap, maxColumnGap)
136
- setShowItems(true)
137
- setWidth(width)
138
- setColumnCount(newColumnCount)
139
- setColumnGapSize(newColumnGapSize)
140
- }
141
- if (throttle === 0) {
142
- task()
143
- } else {
144
- timeout = window.setTimeout(task, throttle || 200)
145
- }
146
- })
147
- observer.observe(ele.current!)
148
-
149
- return () => {
150
- observer.disconnect()
151
- }
152
- }, [itemWidth, throttle, columnGap])
138
+ task()
139
+ }, [])
140
+
141
+ useEffect(() => {
142
+ if (throttle === 0) return task()
143
+ const timeout = setTimeout(task, throttle || 200)
144
+ return () => clearTimeout(timeout)
145
+ }, [width, itemWidth, throttle, columnGap])
153
146
 
154
147
  useEffect(() => {
155
148
  onSizeChange?.({ width, height, itemWidth, itemHeight, columnGap: columnGapSize, columnCount, rowGap: rowGap!, rowCount: contentShownRows, overflow: data.length > contentShownRows * columnCount, itemCount: data.length, maxRows: maxRows ?? null })
@@ -168,46 +161,53 @@ export function Flow<T>(props: FlowProps<T>) {
168
161
  )}
169
162
  style={transformCSSVariable({ height: px(height) }, style)}
170
163
  {...rest}>
171
- {showItems &&
172
- data.map((item, index, arr) => (
164
+ {data.map((item, index, arr) => (
165
+ <div
166
+ key={keyExactor ? keyExactor(item, index, arr) : index}
167
+ className={clsx(
168
+ css`
169
+ position: absolute;
170
+ width: var(--width);
171
+ height: var(--height);
172
+ transition: var(--transition);
173
+ left: 0;
174
+ top: 0;
175
+ transform: translate(var(--left), var(--top));
176
+ `,
177
+ wrapperClassName
178
+ )}
179
+ style={transformCSSVariable(
180
+ {
181
+ width: px(itemWidth),
182
+ height: px(itemHeight),
183
+ transition: transitionDuration === 0 ? "none" : `all ${transitionDuration || 400}ms`,
184
+ left: px(getPosition(index).left),
185
+ top: px(getPosition(index).top)
186
+ },
187
+ wrapperStyle
188
+ )}>
173
189
  <div
174
- key={keyExactor ? keyExactor(item, index, arr) : index}
175
190
  className={clsx(
176
191
  css`
177
- position: absolute;
178
- width: var(--width);
179
- height: var(--height);
180
- transition: var(--transition);
181
- left: 0;
182
- top: 0;
183
- transform: translate(var(--left), var(--top));
192
+ width: 100%;
193
+ height: 100%;
194
+ display: var(--display);
184
195
  `,
185
- wrapperClassName
196
+ containerClassName
186
197
  )}
187
- style={transformCSSVariable(
188
- {
189
- width: px(itemWidth),
190
- height: px(itemHeight),
191
- transition: transitionDuration === 0 ? "none" : `all ${transitionDuration || 400}ms`,
192
- left: px(getPosition(index).left),
193
- top: px(getPosition(index).top)
194
- },
195
- wrapperStyle
196
- )}>
197
- <div
198
- className={clsx(
199
- css`
200
- width: 100%;
201
- height: 100%;
202
- display: var(--display);
203
- `,
204
- containerClassName
205
- )}
206
- style={transformCSSVariable({ display: getHidden(index) ? "none" : "block" }, containerStyle)}>
207
- {render(item, index, arr)}
208
- </div>
198
+ style={transformCSSVariable({ display: getHidden(index) ? "none" : "block" }, containerStyle)}>
199
+ {render(item, index, arr)}
209
200
  </div>
210
- ))}
201
+ </div>
202
+ ))}
211
203
  </div>
212
204
  )
213
205
  }
206
+
207
+ /** 自适应浮动组件 */
208
+ export function Flow<T>(props: FlowProps<T>) {
209
+ const ele = useRef<HTMLDivElement>(null)
210
+ const size = useSize(ele)
211
+ if (!size) return <div ref={ele} {...props} />
212
+ return <RealFlow element={ele} {...props} width={size.width} />
213
+ }