@canlooks/can-ui 0.0.39 → 0.0.41

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.
@@ -19,13 +19,13 @@ size, labelWidth, colon, labelPlacement, disableMargin, disablePadding, span = {
19
19
  labelPlacement ??= context.labelPlacement ?? 'left';
20
20
  disableMargin ??= context.disableMargin;
21
21
  disablePadding ??= context.disablePadding;
22
- const spanNum = (0, utils_1.useResponsiveValue)(span);
22
+ const spanNum = (0, utils_1.useResponsiveValue)(span, variant === 'grid');
23
23
  return variant === 'grid'
24
24
  ? (0, jsx_runtime_1.jsxs)(grid_1.GridItem, { ...props, css: descriptions_style_1.gridItemStyle, className: (0, utils_1.clsx)(descriptions_style_1.classes.item, props.className), span: span, "data-size": size, "data-label-placement": labelPlacement, "data-disable-margin": disableMargin, children: [!!label &&
25
25
  (0, jsx_runtime_1.jsxs)("div", { className: descriptions_style_1.classes.label, style: { width: labelWidth }, children: [label, !!colon && (labelPlacement === 'left' || labelPlacement === 'right') &&
26
26
  (0, jsx_runtime_1.jsx)("div", { className: descriptions_style_1.classes.colon, children: colon })] }), (0, jsx_runtime_1.jsx)("div", { className: descriptions_style_1.classes.content, children: content ?? props.children })] })
27
27
  : labelPlacement === 'top'
28
- ? (0, jsx_runtime_1.jsx)("td", { className: descriptions_style_1.classes.vertical, colSpan: spanNum, children: (0, jsx_runtime_1.jsxs)("div", { className: descriptions_style_1.classes.verticalColWrap, children: [(0, jsx_runtime_1.jsx)("div", { className: `${descriptions_style_1.classes.col} ${descriptions_style_1.classes.labelCol}`, "data-size": size, children: label }), (0, jsx_runtime_1.jsx)("div", { className: `${descriptions_style_1.classes.col} ${descriptions_style_1.classes.contentCol}`, "data-size": size, "data-disable-padding": disablePadding, children: content ?? props.children })] }) })
28
+ ? (0, jsx_runtime_1.jsx)("td", { className: descriptions_style_1.classes.vertical, colSpan: spanNum.current, children: (0, jsx_runtime_1.jsxs)("div", { className: descriptions_style_1.classes.verticalColWrap, children: [(0, jsx_runtime_1.jsx)("div", { className: `${descriptions_style_1.classes.col} ${descriptions_style_1.classes.labelCol}`, "data-size": size, children: label }), (0, jsx_runtime_1.jsx)("div", { className: `${descriptions_style_1.classes.col} ${descriptions_style_1.classes.contentCol}`, "data-size": size, "data-disable-padding": disablePadding, children: content ?? props.children })] }) })
29
29
  : (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [!!label &&
30
- (0, jsx_runtime_1.jsx)("td", { className: `${descriptions_style_1.classes.col} ${descriptions_style_1.classes.labelCol}`, "data-size": size, children: label }), (0, jsx_runtime_1.jsx)("td", { className: `${descriptions_style_1.classes.col} ${descriptions_style_1.classes.contentCol}`, colSpan: spanNum * 2 - 1, "data-size": size, "data-disable-padding": disablePadding, children: content ?? props.children })] });
30
+ (0, jsx_runtime_1.jsx)("td", { className: `${descriptions_style_1.classes.col} ${descriptions_style_1.classes.labelCol}`, "data-size": size, children: label }), (0, jsx_runtime_1.jsx)("td", { className: `${descriptions_style_1.classes.col} ${descriptions_style_1.classes.contentCol}`, colSpan: spanNum.current * 2 - 1, "data-size": size, "data-disable-padding": disablePadding, children: content ?? props.children })] });
31
31
  });
@@ -27,17 +27,17 @@ exports.Descriptions = (0, react_2.memo)(({ size, labelWidth, colon = ':', label
27
27
  // 最后一项沾满剩余行空间
28
28
  flex: i === items.length - 1 ? 1 : void 0, ...itemProps, key: itemProps.key ?? i })) || props.children;
29
29
  };
30
- const columnCountNum = (0, utils_1.useResponsiveValue)(columnCount);
30
+ const columnCountNum = (0, utils_1.useResponsiveValue)(columnCount, variant === 'grid');
31
31
  const renderTableItems = () => {
32
32
  const actualItems = items || react_2.Children.map(props.children, c => (0, react_2.isValidElement)(c) ? c.props : c);
33
33
  if (actualItems?.length) {
34
34
  const rows = [];
35
35
  for (let i = 0, { length } = actualItems; i < length; i++) {
36
- const cols = rows[Math.floor(i / columnCountNum)] ||= [];
36
+ const cols = rows[Math.floor(i / columnCountNum.current)] ||= [];
37
37
  const itemProps = actualItems[i];
38
- cols.push((0, react_1.createElement)(ItemComponent
39
- // 最后一项沾满剩余行空间
40
- , { ...itemProps, key: itemProps.key ?? i, span: i === actualItems.length - 1 ? columnCountNum - i % columnCountNum : itemProps.span, className: (0, utils_1.clsx)(descriptions_style_1.classes.col, itemProps) }));
38
+ cols.push((0, react_1.createElement)(ItemComponent, { ...itemProps, key: itemProps.key ?? i,
39
+ // 最后一项沾满剩余行空间
40
+ span: i === actualItems.length - 1 ? columnCountNum.current - i % columnCountNum.current : itemProps.span, className: (0, utils_1.clsx)(descriptions_style_1.classes.col, itemProps) }));
41
41
  }
42
42
  return rows.map((r, i) => (0, jsx_runtime_1.jsx)("tr", { children: r }, i));
43
43
  }
@@ -35,5 +35,5 @@ exports.SlidableActionsAction = (0, react_1.memo)(({ color = 'default', label, i
35
35
  return ((0, jsx_runtime_1.jsx)("div", { ...props, className: slidableActions_style_1.classes.actionItem, style: {
36
36
  translate: `${currentTranslate}px 0`,
37
37
  backgroundColor
38
- }, children: (0, jsx_runtime_1.jsx)(button_1.Button, { ...buttonProps, ref: (0, utils_1.cloneRef)(buttonProps?.ref, innerButtonRef), className: (0, utils_1.clsx)(slidableActions_style_1.classes.actionItemWrap, buttonProps?.className), prefix: icon, variant: "plain", color: "inverse", children: label }) }));
38
+ }, children: (0, jsx_runtime_1.jsx)(button_1.Button, { ...buttonProps, ref: (0, utils_1.cloneRef)(buttonProps?.ref, innerButtonRef), className: (0, utils_1.clsx)(slidableActions_style_1.classes.actionItemWrap, buttonProps?.className), prefix: icon, variant: "plain", color: "text.primary", children: label }) }));
39
39
  });
@@ -1,10 +1,9 @@
1
- import { ResponsiveProp } from '../../types';
2
- import { TransportStyleProps } from '../transportStyle';
3
- export interface WaterfallProps extends Omit<TransportStyleProps, 'padding' | 'paddingLeft' | 'paddingRight' | 'paddingTop' | 'paddingBottom'> {
1
+ import { DivProps, ResponsiveProp } from '../../types';
2
+ export interface WaterfallProps extends DivProps {
4
3
  /** 布局列数,默认为`{xs: 4}` */
5
4
  columnCount?: ResponsiveProp;
6
5
  gap?: ResponsiveProp;
7
6
  columnGap?: ResponsiveProp;
8
7
  rowGap?: ResponsiveProp;
9
8
  }
10
- export declare function Waterfall({ columnCount, gap, columnGap, rowGap, ...props }: WaterfallProps): import("@emotion/react/jsx-runtime").JSX.Element;
9
+ export declare const Waterfall: ({ columnCount, gap, columnGap, rowGap, ...props }: WaterfallProps) => import("@emotion/react/jsx-runtime").JSX.Element;
@@ -1,39 +1,37 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Waterfall = Waterfall;
3
+ exports.Waterfall = void 0;
4
4
  const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
5
5
  const react_1 = require("react");
6
6
  const waterfall_style_1 = require("./waterfall.style");
7
+ const waterfallItem_1 = require("./waterfallItem");
7
8
  const utils_1 = require("../../utils");
8
- const transportStyle_1 = require("../transportStyle");
9
- function Waterfall({ columnCount = { xs: 4 }, gap = { xs: 0 }, columnGap, rowGap, ...props }) {
9
+ const Waterfall = ({ columnCount = { xs: 4 }, gap = 0, columnGap, rowGap, ...props }) => {
10
10
  columnCount = (0, utils_1.toResponsiveValue)(columnCount);
11
11
  gap = (0, utils_1.toResponsiveValue)(gap);
12
12
  columnGap = (0, utils_1.toResponsiveValue)(columnGap) ?? gap;
13
13
  rowGap = (0, utils_1.toResponsiveValue)(rowGap) ?? gap;
14
- const syncColumnCount = (0, utils_1.useSync)((0, utils_1.useResponsiveValue)(columnCount));
15
- const container = (0, react_1.useRef)(null);
14
+ const columnCountNum = (0, utils_1.useResponsiveValue)(columnCount);
15
+ const containerRef = (0, react_1.useRef)(null);
16
16
  const elements = (0, react_1.useRef)([]);
17
17
  elements.current = [];
18
18
  const computeItemOrder = () => {
19
- const heights = Array(syncColumnCount.current).fill(0);
19
+ const heights = Array(columnCountNum.current).fill(0);
20
20
  elements.current.forEach(el => {
21
- if (el) {
22
- const order = heights.indexOf(Math.min(...heights));
23
- heights[order] += el.offsetHeight;
24
- el.style.order = order + '';
25
- }
21
+ const order = heights.indexOf(Math.min(...heights));
22
+ heights[order] += el.offsetHeight;
23
+ el.style.order = order + '';
26
24
  });
27
- container.current.style.height = Math.max(...heights) + 1 + 'px';
25
+ containerRef.current.style.height = Math.max(...heights) + 1 + 'px';
28
26
  };
29
27
  const resizeObserver = (0, react_1.useRef)(void 0);
30
28
  resizeObserver.current ||= new ResizeObserver(computeItemOrder);
31
- const setItemRefAndObserve = (r) => {
32
- if (r) {
33
- elements.current.push(r);
34
- resizeObserver.current.observe(r);
35
- }
36
- };
29
+ const pendingItems = [];
30
+ (0, react_1.useLayoutEffect)(() => {
31
+ Promise.all(pendingItems).then(() => {
32
+ elements.current.forEach(el => resizeObserver.current.observe(el));
33
+ });
34
+ });
37
35
  const isInitialized = (0, react_1.useRef)(false);
38
36
  (0, react_1.useEffect)(() => {
39
37
  if (!isInitialized.current) {
@@ -41,9 +39,19 @@ function Waterfall({ columnCount = { xs: 4 }, gap = { xs: 0 }, columnGap, rowGap
41
39
  return;
42
40
  }
43
41
  computeItemOrder();
44
- }, [syncColumnCount.current]);
45
- return ((0, jsx_runtime_1.jsx)(transportStyle_1.TransportStyle, { ...props, ref: (0, utils_1.cloneRef)(props.ref, container), css: (0, waterfall_style_1.useStyle)({ columnCount, columnGap, rowGap }), className: (0, utils_1.clsx)(waterfall_style_1.classes.root, props.className), children: react_1.Children.map(props.children, (child, i) => {
46
- const key = (0, react_1.isValidElement)(child) ? child.key ?? i : i;
47
- return ((0, jsx_runtime_1.jsx)("div", { ref: setItemRefAndObserve, className: waterfall_style_1.classes.item, children: child }, key));
42
+ }, [columnCountNum.current]);
43
+ (0, react_1.useEffect)(() => () => {
44
+ resizeObserver.current.disconnect();
45
+ }, []);
46
+ return ((0, jsx_runtime_1.jsx)("div", { ...props, ref: (0, utils_1.cloneRef)(containerRef, props.ref), css: (0, waterfall_style_1.useStyle)({ columnCount, columnGap, rowGap }), className: (0, utils_1.clsx)(waterfall_style_1.classes.root, props.className), children: react_1.Children.map(props.children, child => {
47
+ if (!(0, react_1.isValidElement)(child)) {
48
+ throw Error('Children of <Waterfall> must be element');
49
+ }
50
+ let onLoad;
51
+ pendingItems.push(new Promise(r => onLoad = r));
52
+ return ((0, jsx_runtime_1.jsx)(waterfallItem_1.WaterfallItem, { ref: r => {
53
+ r && elements.current.push(r);
54
+ }, child: child, onLoad: onLoad }));
48
55
  }) }));
49
- }
56
+ };
57
+ exports.Waterfall = Waterfall;
@@ -2,9 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.classes = void 0;
4
4
  exports.useStyle = useStyle;
5
- const react_1 = require("@emotion/react");
6
5
  const utils_1 = require("../../utils");
7
- exports.classes = (0, utils_1.defineInnerClasses)('waterfall', [
6
+ const react_1 = require("@emotion/react");
7
+ exports.classes = (0, utils_1.defineClasses)('waterfall', [
8
8
  'item'
9
9
  ]);
10
10
  function useStyle({ columnCount, columnGap, rowGap }) {
@@ -19,17 +19,5 @@ function useStyle({ columnCount, columnGap, rowGap }) {
19
19
  flex-direction: column;
20
20
  flex-wrap: wrap;
21
21
  column-gap: var(--waterfall-column-gap);
22
- //row-gap: var(--waterfall-row-gap);
23
- //overflow: hidden;
24
-
25
- .${exports.classes.item} {
26
- width: calc((100% - var(--waterfall-column-gap) * (var(--waterfall-columnCount) - 1)) / var(--waterfall-columnCount));
27
- padding-bottom: var(--waterfall-row-gap);
28
-
29
- > img {
30
- width: 100%;
31
- display: flex;
32
- }
33
- }
34
22
  `);
35
23
  }
@@ -0,0 +1,7 @@
1
+ import { ReactElement, Ref } from 'react';
2
+ export type WaterfallItemProps = {
3
+ ref: Ref<HTMLElement>;
4
+ onLoad(): void;
5
+ child: ReactElement<any>;
6
+ };
7
+ export declare const WaterfallItem: ({ ref, onLoad, child }: WaterfallItemProps) => ReactElement<any, string | import("react").JSXElementConstructor<any>>;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WaterfallItem = void 0;
4
+ const react_1 = require("react");
5
+ const waterfall_style_1 = require("./waterfall.style");
6
+ const utils_1 = require("../../utils");
7
+ const WaterfallItem = ({ ref, onLoad, child }) => {
8
+ const innerRef = (0, react_1.useRef)(null);
9
+ (0, react_1.useLayoutEffect)(() => {
10
+ const el = innerRef.current;
11
+ if (!el) {
12
+ throw Error(`Children of <Waterfall> must expose 'ref' prop`);
13
+ }
14
+ const imgTags = el.querySelectorAll('img');
15
+ if (!imgTags.length) {
16
+ onLoad();
17
+ return;
18
+ }
19
+ Promise.all([...imgTags].map(img => {
20
+ if (!img.complete) {
21
+ return new Promise(resolve => {
22
+ img.addEventListener('load', resolve, { once: true });
23
+ });
24
+ }
25
+ })).then(() => onLoad());
26
+ });
27
+ const clonedRef = (0, react_1.useCallback)((0, utils_1.cloneRef)(child.props.ref, innerRef, ref), [child.props.ref]);
28
+ return (0, react_1.cloneElement)(child, {
29
+ ref: clonedRef,
30
+ className: (0, utils_1.clsx)(waterfall_style_1.classes.item, child.props.className),
31
+ style: {
32
+ width: 'calc((100% - var(--waterfall-column-gap) * (var(--waterfall-columnCount) - 1)) / var(--waterfall-columnCount))',
33
+ paddingBottom: 'var(--waterfall-row-gap)',
34
+ ...child.props.style
35
+ }
36
+ });
37
+ };
38
+ exports.WaterfallItem = WaterfallItem;
@@ -84,8 +84,9 @@ export declare function toResponsiveValue<T = number>(prop: ResponsiveProp<T>):
84
84
  /**
85
85
  * 使用hooks监听响应式值的变化,通常用于css不能满足需求的情况
86
86
  * @param prop
87
+ * @param disabled
87
88
  */
88
- export declare function useResponsiveValue<T = number>(prop: ResponsiveProp<T>): NonNullable<T>;
89
+ export declare function useResponsiveValue<T = number>(prop: ResponsiveProp<T>, disabled?: boolean): import("react").RefObject<NonNullable<T>>;
89
90
  /**
90
91
  * 使用状态对应的颜色
91
92
  * @param status
@@ -18,6 +18,7 @@ const react_1 = require("react");
18
18
  const theme_1 = require("../components/theme");
19
19
  const utils_1 = require("./utils");
20
20
  const color_1 = tslib_1.__importDefault(require("color"));
21
+ const hooks_1 = require("./hooks");
21
22
  /**
22
23
  * 为类名添加统一前缀,通常为内部使用
23
24
  * @param prefixName
@@ -186,14 +187,16 @@ function toResponsiveValue(prop) {
186
187
  /**
187
188
  * 使用hooks监听响应式值的变化,通常用于css不能满足需求的情况
188
189
  * @param prop
190
+ * @param disabled
189
191
  */
190
- function useResponsiveValue(prop) {
192
+ function useResponsiveValue(prop, disabled = false) {
191
193
  const responsiveObj = toResponsiveValue(prop);
192
194
  const { breakpoints } = (0, theme_1.useTheme)();
195
+ const syncBreakpoints = (0, hooks_1.useSync)(breakpoints);
193
196
  const fn = () => {
194
197
  let maxBreakpoint = 'xs';
195
- for (const k in breakpoints) {
196
- if (window.innerWidth < breakpoints[k]) {
198
+ for (const k in syncBreakpoints.current) {
199
+ if (window.innerWidth < syncBreakpoints.current[k]) {
197
200
  break;
198
201
  }
199
202
  if (k in responsiveObj) {
@@ -202,8 +205,11 @@ function useResponsiveValue(prop) {
202
205
  }
203
206
  return responsiveObj[maxBreakpoint];
204
207
  };
205
- const [value, setValue] = (0, react_1.useState)(fn);
208
+ const [value, setValue] = (0, hooks_1.useDerivedState)(fn, [breakpoints]);
206
209
  (0, react_1.useEffect)(() => {
210
+ if (disabled) {
211
+ return;
212
+ }
207
213
  const resize = () => {
208
214
  setValue(fn());
209
215
  };
@@ -211,7 +217,7 @@ function useResponsiveValue(prop) {
211
217
  return () => {
212
218
  removeEventListener('resize', resize);
213
219
  };
214
- }, []);
220
+ }, [disabled]);
215
221
  return value;
216
222
  }
217
223
  /**
@@ -16,13 +16,13 @@ size, labelWidth, colon, labelPlacement, disableMargin, disablePadding, span = {
16
16
  labelPlacement ??= context.labelPlacement ?? 'left';
17
17
  disableMargin ??= context.disableMargin;
18
18
  disablePadding ??= context.disablePadding;
19
- const spanNum = useResponsiveValue(span);
19
+ const spanNum = useResponsiveValue(span, variant === 'grid');
20
20
  return variant === 'grid'
21
21
  ? _jsxs(GridItem, { ...props, css: gridItemStyle, className: clsx(classes.item, props.className), span: span, "data-size": size, "data-label-placement": labelPlacement, "data-disable-margin": disableMargin, children: [!!label &&
22
22
  _jsxs("div", { className: classes.label, style: { width: labelWidth }, children: [label, !!colon && (labelPlacement === 'left' || labelPlacement === 'right') &&
23
23
  _jsx("div", { className: classes.colon, children: colon })] }), _jsx("div", { className: classes.content, children: content ?? props.children })] })
24
24
  : labelPlacement === 'top'
25
- ? _jsx("td", { className: classes.vertical, colSpan: spanNum, children: _jsxs("div", { className: classes.verticalColWrap, children: [_jsx("div", { className: `${classes.col} ${classes.labelCol}`, "data-size": size, children: label }), _jsx("div", { className: `${classes.col} ${classes.contentCol}`, "data-size": size, "data-disable-padding": disablePadding, children: content ?? props.children })] }) })
25
+ ? _jsx("td", { className: classes.vertical, colSpan: spanNum.current, children: _jsxs("div", { className: classes.verticalColWrap, children: [_jsx("div", { className: `${classes.col} ${classes.labelCol}`, "data-size": size, children: label }), _jsx("div", { className: `${classes.col} ${classes.contentCol}`, "data-size": size, "data-disable-padding": disablePadding, children: content ?? props.children })] }) })
26
26
  : _jsxs(_Fragment, { children: [!!label &&
27
- _jsx("td", { className: `${classes.col} ${classes.labelCol}`, "data-size": size, children: label }), _jsx("td", { className: `${classes.col} ${classes.contentCol}`, colSpan: spanNum * 2 - 1, "data-size": size, "data-disable-padding": disablePadding, children: content ?? props.children })] });
27
+ _jsx("td", { className: `${classes.col} ${classes.labelCol}`, "data-size": size, children: label }), _jsx("td", { className: `${classes.col} ${classes.contentCol}`, colSpan: spanNum.current * 2 - 1, "data-size": size, "data-disable-padding": disablePadding, children: content ?? props.children })] });
28
28
  });
@@ -23,17 +23,17 @@ export const Descriptions = memo(({ size, labelWidth, colon = ':', labelPlacemen
23
23
  // 最后一项沾满剩余行空间
24
24
  flex: i === items.length - 1 ? 1 : void 0, ...itemProps, key: itemProps.key ?? i })) || props.children;
25
25
  };
26
- const columnCountNum = useResponsiveValue(columnCount);
26
+ const columnCountNum = useResponsiveValue(columnCount, variant === 'grid');
27
27
  const renderTableItems = () => {
28
28
  const actualItems = items || Children.map(props.children, c => isValidElement(c) ? c.props : c);
29
29
  if (actualItems?.length) {
30
30
  const rows = [];
31
31
  for (let i = 0, { length } = actualItems; i < length; i++) {
32
- const cols = rows[Math.floor(i / columnCountNum)] ||= [];
32
+ const cols = rows[Math.floor(i / columnCountNum.current)] ||= [];
33
33
  const itemProps = actualItems[i];
34
- cols.push(_createElement(ItemComponent
35
- // 最后一项沾满剩余行空间
36
- , { ...itemProps, key: itemProps.key ?? i, span: i === actualItems.length - 1 ? columnCountNum - i % columnCountNum : itemProps.span, className: clsx(classes.col, itemProps) }));
34
+ cols.push(_createElement(ItemComponent, { ...itemProps, key: itemProps.key ?? i,
35
+ // 最后一项沾满剩余行空间
36
+ span: i === actualItems.length - 1 ? columnCountNum.current - i % columnCountNum.current : itemProps.span, className: clsx(classes.col, itemProps) }));
37
37
  }
38
38
  return rows.map((r, i) => _jsx("tr", { children: r }, i));
39
39
  }
@@ -32,5 +32,5 @@ export const SlidableActionsAction = memo(({ color = 'default', label, icon, but
32
32
  return (_jsx("div", { ...props, className: classes.actionItem, style: {
33
33
  translate: `${currentTranslate}px 0`,
34
34
  backgroundColor
35
- }, children: _jsx(Button, { ...buttonProps, ref: cloneRef(buttonProps?.ref, innerButtonRef), className: clsx(classes.actionItemWrap, buttonProps?.className), prefix: icon, variant: "plain", color: "inverse", children: label }) }));
35
+ }, children: _jsx(Button, { ...buttonProps, ref: cloneRef(buttonProps?.ref, innerButtonRef), className: clsx(classes.actionItemWrap, buttonProps?.className), prefix: icon, variant: "plain", color: "text.primary", children: label }) }));
36
36
  });
@@ -1,10 +1,9 @@
1
- import { ResponsiveProp } from '../../types';
2
- import { TransportStyleProps } from '../transportStyle';
3
- export interface WaterfallProps extends Omit<TransportStyleProps, 'padding' | 'paddingLeft' | 'paddingRight' | 'paddingTop' | 'paddingBottom'> {
1
+ import { DivProps, ResponsiveProp } from '../../types';
2
+ export interface WaterfallProps extends DivProps {
4
3
  /** 布局列数,默认为`{xs: 4}` */
5
4
  columnCount?: ResponsiveProp;
6
5
  gap?: ResponsiveProp;
7
6
  columnGap?: ResponsiveProp;
8
7
  rowGap?: ResponsiveProp;
9
8
  }
10
- export declare function Waterfall({ columnCount, gap, columnGap, rowGap, ...props }: WaterfallProps): import("@emotion/react/jsx-runtime").JSX.Element;
9
+ export declare const Waterfall: ({ columnCount, gap, columnGap, rowGap, ...props }: WaterfallProps) => import("@emotion/react/jsx-runtime").JSX.Element;
@@ -1,36 +1,34 @@
1
1
  import { jsx as _jsx } from "@emotion/react/jsx-runtime";
2
- import { Children, isValidElement, useEffect, useRef } from 'react';
2
+ import { Children, isValidElement, useEffect, useLayoutEffect, useRef } from 'react';
3
3
  import { classes, useStyle } from './waterfall.style';
4
- import { clsx, cloneRef, toResponsiveValue, useResponsiveValue, useSync } from '../../utils';
5
- import { TransportStyle } from '../transportStyle';
6
- export function Waterfall({ columnCount = { xs: 4 }, gap = { xs: 0 }, columnGap, rowGap, ...props }) {
4
+ import { WaterfallItem } from './waterfallItem';
5
+ import { cloneRef, clsx, toResponsiveValue, useResponsiveValue } from '../../utils';
6
+ export const Waterfall = ({ columnCount = { xs: 4 }, gap = 0, columnGap, rowGap, ...props }) => {
7
7
  columnCount = toResponsiveValue(columnCount);
8
8
  gap = toResponsiveValue(gap);
9
9
  columnGap = toResponsiveValue(columnGap) ?? gap;
10
10
  rowGap = toResponsiveValue(rowGap) ?? gap;
11
- const syncColumnCount = useSync(useResponsiveValue(columnCount));
12
- const container = useRef(null);
11
+ const columnCountNum = useResponsiveValue(columnCount);
12
+ const containerRef = useRef(null);
13
13
  const elements = useRef([]);
14
14
  elements.current = [];
15
15
  const computeItemOrder = () => {
16
- const heights = Array(syncColumnCount.current).fill(0);
16
+ const heights = Array(columnCountNum.current).fill(0);
17
17
  elements.current.forEach(el => {
18
- if (el) {
19
- const order = heights.indexOf(Math.min(...heights));
20
- heights[order] += el.offsetHeight;
21
- el.style.order = order + '';
22
- }
18
+ const order = heights.indexOf(Math.min(...heights));
19
+ heights[order] += el.offsetHeight;
20
+ el.style.order = order + '';
23
21
  });
24
- container.current.style.height = Math.max(...heights) + 1 + 'px';
22
+ containerRef.current.style.height = Math.max(...heights) + 1 + 'px';
25
23
  };
26
24
  const resizeObserver = useRef(void 0);
27
25
  resizeObserver.current ||= new ResizeObserver(computeItemOrder);
28
- const setItemRefAndObserve = (r) => {
29
- if (r) {
30
- elements.current.push(r);
31
- resizeObserver.current.observe(r);
32
- }
33
- };
26
+ const pendingItems = [];
27
+ useLayoutEffect(() => {
28
+ Promise.all(pendingItems).then(() => {
29
+ elements.current.forEach(el => resizeObserver.current.observe(el));
30
+ });
31
+ });
34
32
  const isInitialized = useRef(false);
35
33
  useEffect(() => {
36
34
  if (!isInitialized.current) {
@@ -38,9 +36,18 @@ export function Waterfall({ columnCount = { xs: 4 }, gap = { xs: 0 }, columnGap,
38
36
  return;
39
37
  }
40
38
  computeItemOrder();
41
- }, [syncColumnCount.current]);
42
- return (_jsx(TransportStyle, { ...props, ref: cloneRef(props.ref, container), css: useStyle({ columnCount, columnGap, rowGap }), className: clsx(classes.root, props.className), children: Children.map(props.children, (child, i) => {
43
- const key = isValidElement(child) ? child.key ?? i : i;
44
- return (_jsx("div", { ref: setItemRefAndObserve, className: classes.item, children: child }, key));
39
+ }, [columnCountNum.current]);
40
+ useEffect(() => () => {
41
+ resizeObserver.current.disconnect();
42
+ }, []);
43
+ return (_jsx("div", { ...props, ref: cloneRef(containerRef, props.ref), css: useStyle({ columnCount, columnGap, rowGap }), className: clsx(classes.root, props.className), children: Children.map(props.children, child => {
44
+ if (!isValidElement(child)) {
45
+ throw Error('Children of <Waterfall> must be element');
46
+ }
47
+ let onLoad;
48
+ pendingItems.push(new Promise(r => onLoad = r));
49
+ return (_jsx(WaterfallItem, { ref: r => {
50
+ r && elements.current.push(r);
51
+ }, child: child, onLoad: onLoad }));
45
52
  }) }));
46
- }
53
+ };
@@ -1,6 +1,6 @@
1
+ import { defineClasses, responsiveVariables, useCss } from '../../utils';
1
2
  import { css } from '@emotion/react';
2
- import { defineInnerClasses, responsiveVariables, useCss } from '../../utils';
3
- export const classes = defineInnerClasses('waterfall', [
3
+ export const classes = defineClasses('waterfall', [
4
4
  'item'
5
5
  ]);
6
6
  export function useStyle({ columnCount, columnGap, rowGap }) {
@@ -15,17 +15,5 @@ export function useStyle({ columnCount, columnGap, rowGap }) {
15
15
  flex-direction: column;
16
16
  flex-wrap: wrap;
17
17
  column-gap: var(--waterfall-column-gap);
18
- //row-gap: var(--waterfall-row-gap);
19
- //overflow: hidden;
20
-
21
- .${classes.item} {
22
- width: calc((100% - var(--waterfall-column-gap) * (var(--waterfall-columnCount) - 1)) / var(--waterfall-columnCount));
23
- padding-bottom: var(--waterfall-row-gap);
24
-
25
- > img {
26
- width: 100%;
27
- display: flex;
28
- }
29
- }
30
18
  `);
31
19
  }
@@ -0,0 +1,7 @@
1
+ import { ReactElement, Ref } from 'react';
2
+ export type WaterfallItemProps = {
3
+ ref: Ref<HTMLElement>;
4
+ onLoad(): void;
5
+ child: ReactElement<any>;
6
+ };
7
+ export declare const WaterfallItem: ({ ref, onLoad, child }: WaterfallItemProps) => ReactElement<any, string | import("react").JSXElementConstructor<any>>;
@@ -0,0 +1,34 @@
1
+ import { cloneElement, useCallback, useLayoutEffect, useRef } from 'react';
2
+ import { classes } from './waterfall.style';
3
+ import { cloneRef, clsx } from '../../utils';
4
+ export const WaterfallItem = ({ ref, onLoad, child }) => {
5
+ const innerRef = useRef(null);
6
+ useLayoutEffect(() => {
7
+ const el = innerRef.current;
8
+ if (!el) {
9
+ throw Error(`Children of <Waterfall> must expose 'ref' prop`);
10
+ }
11
+ const imgTags = el.querySelectorAll('img');
12
+ if (!imgTags.length) {
13
+ onLoad();
14
+ return;
15
+ }
16
+ Promise.all([...imgTags].map(img => {
17
+ if (!img.complete) {
18
+ return new Promise(resolve => {
19
+ img.addEventListener('load', resolve, { once: true });
20
+ });
21
+ }
22
+ })).then(() => onLoad());
23
+ });
24
+ const clonedRef = useCallback(cloneRef(child.props.ref, innerRef, ref), [child.props.ref]);
25
+ return cloneElement(child, {
26
+ ref: clonedRef,
27
+ className: clsx(classes.item, child.props.className),
28
+ style: {
29
+ width: 'calc((100% - var(--waterfall-column-gap) * (var(--waterfall-columnCount) - 1)) / var(--waterfall-columnCount))',
30
+ paddingBottom: 'var(--waterfall-row-gap)',
31
+ ...child.props.style
32
+ }
33
+ });
34
+ };
@@ -84,8 +84,9 @@ export declare function toResponsiveValue<T = number>(prop: ResponsiveProp<T>):
84
84
  /**
85
85
  * 使用hooks监听响应式值的变化,通常用于css不能满足需求的情况
86
86
  * @param prop
87
+ * @param disabled
87
88
  */
88
- export declare function useResponsiveValue<T = number>(prop: ResponsiveProp<T>): NonNullable<T>;
89
+ export declare function useResponsiveValue<T = number>(prop: ResponsiveProp<T>, disabled?: boolean): import("react").RefObject<NonNullable<T>>;
89
90
  /**
90
91
  * 使用状态对应的颜色
91
92
  * @param status
@@ -1,7 +1,8 @@
1
- import { useEffect, useMemo, useState } from 'react';
1
+ import { useEffect, useMemo } from 'react';
2
2
  import { defaultBreakpoints, useTheme } from '../components/theme';
3
3
  import { humpToSegmented, isUnset } from './utils';
4
4
  import Color from 'color';
5
+ import { useDerivedState, useSync } from './hooks';
5
6
  /**
6
7
  * 为类名添加统一前缀,通常为内部使用
7
8
  * @param prefixName
@@ -170,14 +171,16 @@ export function toResponsiveValue(prop) {
170
171
  /**
171
172
  * 使用hooks监听响应式值的变化,通常用于css不能满足需求的情况
172
173
  * @param prop
174
+ * @param disabled
173
175
  */
174
- export function useResponsiveValue(prop) {
176
+ export function useResponsiveValue(prop, disabled = false) {
175
177
  const responsiveObj = toResponsiveValue(prop);
176
178
  const { breakpoints } = useTheme();
179
+ const syncBreakpoints = useSync(breakpoints);
177
180
  const fn = () => {
178
181
  let maxBreakpoint = 'xs';
179
- for (const k in breakpoints) {
180
- if (window.innerWidth < breakpoints[k]) {
182
+ for (const k in syncBreakpoints.current) {
183
+ if (window.innerWidth < syncBreakpoints.current[k]) {
181
184
  break;
182
185
  }
183
186
  if (k in responsiveObj) {
@@ -186,8 +189,11 @@ export function useResponsiveValue(prop) {
186
189
  }
187
190
  return responsiveObj[maxBreakpoint];
188
191
  };
189
- const [value, setValue] = useState(fn);
192
+ const [value, setValue] = useDerivedState(fn, [breakpoints]);
190
193
  useEffect(() => {
194
+ if (disabled) {
195
+ return;
196
+ }
191
197
  const resize = () => {
192
198
  setValue(fn());
193
199
  };
@@ -195,7 +201,7 @@ export function useResponsiveValue(prop) {
195
201
  return () => {
196
202
  removeEventListener('resize', resize);
197
203
  };
198
- }, []);
204
+ }, [disabled]);
199
205
  return value;
200
206
  }
201
207
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canlooks/can-ui",
3
- "version": "0.0.39",
3
+ "version": "0.0.41",
4
4
  "author": "C.CanLiang <canlooks@gmail.com>",
5
5
  "description": "My ui framework",
6
6
  "license": "MIT",