@canlooks/can-ui 0.0.67 → 0.0.68

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 (53) hide show
  1. package/dist/cjs/components/anchorList/anchorList.js +26 -20
  2. package/dist/cjs/components/cascade/cascade.js +2 -2
  3. package/dist/cjs/components/curd/curdColumnConfig.js +2 -1
  4. package/dist/cjs/components/dataGrid/dataGrid.js +3 -2
  5. package/dist/cjs/components/gallery/gallery.js +2 -2
  6. package/dist/cjs/components/menu/menu.js +2 -2
  7. package/dist/cjs/components/overlayBase/overlayBase.d.ts +3 -1
  8. package/dist/cjs/components/overlayBase/overlayBase.js +5 -5
  9. package/dist/cjs/components/popper/popper.d.ts +3 -1
  10. package/dist/cjs/components/popper/popper.js +6 -6
  11. package/dist/cjs/components/progress/progress.js +4 -2
  12. package/dist/cjs/components/snackbarBase/snackbarBase.d.ts +3 -2
  13. package/dist/cjs/components/snackbarBase/snackbarBase.js +3 -3
  14. package/dist/cjs/components/theme/themeVariables.d.ts +1 -0
  15. package/dist/cjs/components/theme/themeVariables.js +1 -0
  16. package/dist/cjs/components/touchRipple/index.d.ts +1 -0
  17. package/dist/cjs/components/touchRipple/index.js +1 -0
  18. package/dist/cjs/components/touchRipple/touchRipple.d.ts +6 -14
  19. package/dist/cjs/components/touchRipple/touchRipple.js +54 -39
  20. package/dist/cjs/components/touchRipple/touchRipple.style.js +25 -27
  21. package/dist/cjs/components/transfer/transfer.js +2 -2
  22. package/dist/cjs/components/transfer/transferPanel.js +2 -1
  23. package/dist/cjs/components/upload/upload.js +2 -2
  24. package/dist/cjs/components/waterfall/waterfallItem.js +1 -1
  25. package/dist/cjs/utils/hooks.d.ts +8 -0
  26. package/dist/cjs/utils/hooks.js +22 -0
  27. package/dist/esm/components/anchorList/anchorList.js +26 -20
  28. package/dist/esm/components/cascade/cascade.js +2 -2
  29. package/dist/esm/components/curd/curdColumnConfig.js +2 -1
  30. package/dist/esm/components/dataGrid/dataGrid.js +3 -2
  31. package/dist/esm/components/gallery/gallery.js +2 -2
  32. package/dist/esm/components/menu/menu.js +2 -2
  33. package/dist/esm/components/overlayBase/overlayBase.d.ts +3 -1
  34. package/dist/esm/components/overlayBase/overlayBase.js +6 -6
  35. package/dist/esm/components/popper/popper.d.ts +3 -1
  36. package/dist/esm/components/popper/popper.js +7 -7
  37. package/dist/esm/components/progress/progress.js +4 -2
  38. package/dist/esm/components/snackbarBase/snackbarBase.d.ts +3 -2
  39. package/dist/esm/components/snackbarBase/snackbarBase.js +4 -4
  40. package/dist/esm/components/theme/themeVariables.d.ts +1 -0
  41. package/dist/esm/components/theme/themeVariables.js +1 -0
  42. package/dist/esm/components/touchRipple/index.d.ts +1 -0
  43. package/dist/esm/components/touchRipple/index.js +1 -0
  44. package/dist/esm/components/touchRipple/touchRipple.d.ts +6 -14
  45. package/dist/esm/components/touchRipple/touchRipple.js +54 -39
  46. package/dist/esm/components/touchRipple/touchRipple.style.js +25 -27
  47. package/dist/esm/components/transfer/transfer.js +2 -2
  48. package/dist/esm/components/transfer/transferPanel.js +2 -1
  49. package/dist/esm/components/upload/upload.js +2 -2
  50. package/dist/esm/components/waterfall/waterfallItem.js +2 -2
  51. package/dist/esm/utils/hooks.d.ts +8 -0
  52. package/dist/esm/utils/hooks.js +21 -0
  53. package/package.json +1 -1
@@ -6,7 +6,7 @@ const waterfall_style_1 = require("./waterfall.style");
6
6
  const utils_1 = require("../../utils");
7
7
  const WaterfallItem = ({ ref, onLoad, child }) => {
8
8
  const innerRef = (0, react_1.useRef)(null);
9
- (0, react_1.useLayoutEffect)(() => {
9
+ (0, react_1.useEffect)(() => {
10
10
  const el = innerRef.current;
11
11
  if (!el) {
12
12
  throw Error(`Children of <Waterfall> must expose 'ref' prop`);
@@ -1,4 +1,5 @@
1
1
  import { RefObject, Dispatch, SetStateAction } from 'react';
2
+ import { DefineElement } from '../types';
2
3
  /**
3
4
  * 获取渲染前的值
4
5
  * @param value
@@ -48,3 +49,10 @@ export declare function useLoading<A extends any[], R>(fn: (...args: A) => R | P
48
49
  (...args: A) => Promise<R>,
49
50
  Dispatch<SetStateAction<boolean>>
50
51
  ];
52
+ /**
53
+ * 获取容器元素,通常用于`container`或`effectContainer`属性
54
+ * @param container
55
+ * @param effectContainer
56
+ * @param defaultContainer 默认为`document.body`
57
+ */
58
+ export declare function useContainer<T extends HTMLElement | null>(container?: DefineElement<T>, effectContainer?: DefineElement<T>, defaultContainer?: T): RefObject<T>;
@@ -8,6 +8,7 @@ exports.useDerivedState = useDerivedState;
8
8
  exports.useUnmounted = useUnmounted;
9
9
  exports.useControlled = useControlled;
10
10
  exports.useLoading = useLoading;
11
+ exports.useContainer = useContainer;
11
12
  const react_1 = require("react");
12
13
  const utils_1 = require("./utils");
13
14
  /**
@@ -123,3 +124,24 @@ function useLoading(fn, referredLoading = false) {
123
124
  setLoading
124
125
  ];
125
126
  }
127
+ /**
128
+ * 获取容器元素,通常用于`container`或`effectContainer`属性
129
+ * @param container
130
+ * @param effectContainer
131
+ * @param defaultContainer 默认为`document.body`
132
+ */
133
+ function useContainer(container, effectContainer, defaultContainer = document.body) {
134
+ const [containerEl, setContainerEl] = useDerivedState(prev => {
135
+ if (container) {
136
+ return typeof container === 'function' ? container() : container;
137
+ }
138
+ return prev || defaultContainer;
139
+ }, [container, defaultContainer]);
140
+ (0, react_1.useEffect)(() => {
141
+ if (effectContainer) {
142
+ const el = typeof effectContainer === 'function' ? effectContainer() : effectContainer;
143
+ setContainerEl(el);
144
+ }
145
+ }, []);
146
+ return containerEl;
147
+ }
@@ -18,10 +18,11 @@ export const AnchorList = memo(({ anchors, renderAnchorItem, indent = 24, scroll
18
18
  });
19
19
  const scrollToId = (id) => {
20
20
  const targetEl = document.getElementById(id);
21
- if (!targetEl) {
21
+ const scrollerEl = getScroller();
22
+ if (!targetEl || !scrollerEl) {
22
23
  return false;
23
24
  }
24
- getScroller().scrollTo({
25
+ scrollerEl.scrollTo({
25
26
  top: targetEl.offsetTop - offset,
26
27
  // 初始化之前无需平滑滚动
27
28
  behavior: initialized.current ? scrollBehavior : 'instant'
@@ -50,29 +51,34 @@ export const AnchorList = memo(({ anchors, renderAnchorItem, indent = 24, scroll
50
51
  * 监听滚动设置高亮
51
52
  */
52
53
  const getScroller = () => {
53
- return (typeof scroller === 'function' ? scroller() : scroller) || document.documentElement;
54
+ if (scroller) {
55
+ return typeof scroller === 'function' ? scroller() : scroller;
56
+ }
57
+ return document.documentElement;
54
58
  };
55
59
  const [activeId, setActiveId] = useSyncState();
56
60
  useEffect(() => {
57
61
  const scrollerEl = getScroller();
58
- const scroll = () => {
59
- let nearest;
60
- let minDistance = Infinity;
61
- const targets = anchors?.map(item => document.getElementById(item.id));
62
- targets?.forEach(target => {
63
- const top = target?.getBoundingClientRect().top;
64
- if (typeof top === 'number') {
65
- const distance = Math.abs(top - offset);
66
- if (distance < minDistance) {
67
- minDistance = distance;
68
- nearest = target;
62
+ if (scrollerEl) {
63
+ const scroll = () => {
64
+ let nearest;
65
+ let minDistance = Infinity;
66
+ const targets = anchors?.map(item => document.getElementById(item.id));
67
+ targets?.forEach(target => {
68
+ const top = target?.getBoundingClientRect().top;
69
+ if (typeof top === 'number') {
70
+ const distance = Math.abs(top - offset);
71
+ if (distance < minDistance) {
72
+ minDistance = distance;
73
+ nearest = target;
74
+ }
69
75
  }
70
- }
71
- });
72
- nearest && setActiveId(nearest.id);
73
- };
74
- !activeId.current && scroll();
75
- return listenAllPredecessorsScroll(scrollerEl === document.documentElement ? document : scrollerEl, scroll);
76
+ });
77
+ nearest && setActiveId(nearest.id);
78
+ };
79
+ !activeId.current && scroll();
80
+ return listenAllPredecessorsScroll(scrollerEl === document.documentElement ? document : scrollerEl, scroll);
81
+ }
76
82
  }, [anchors, scroller, offset]);
77
83
  const [animating, setAnimating] = useDerivedState(prev => typeof prev !== 'undefined', [activeId.current]);
78
84
  return (_jsxs(Flex, { direction: "column", alignItems: "flex-start", ...props, css: style, className: clsx(classes.root, props.className), "data-animating": animating.current, children: [anchors?.map(item => {
@@ -16,7 +16,7 @@ const CascadeContext = createContext({});
16
16
  export function useCascadeContext() {
17
17
  return useContext(CascadeContext);
18
18
  }
19
- export const Cascade = memo(({ inputProps, defaultOpen = false, open, onOpenChange, children, loadOptions, multiple = false, showCheckbox = !!multiple, defaultValue = [], value, onChange, renderBackfill, searchable, defaultSearchValue = '', searchValue, onSearchChange, searchInputProps, popperProps, popperRef, clearable = !!multiple, integration = 'deepest',
19
+ export const Cascade = memo(({ inputProps, defaultOpen = false, open, onOpenChange, children, loadOptions, multiple = false, showCheckbox = !!multiple, defaultValue, value, onChange, renderBackfill, searchable, defaultSearchValue = '', searchValue, onSearchChange, searchInputProps, popperProps, popperRef, clearable = !!multiple, integration = 'deepest',
20
20
  // 共享属性,从OptionsBaseSharedProps继承
21
21
  loading, options, labelKey = 'label', primaryKey = 'value', childrenKey = 'children', searchTokenKey = 'searchToken', ...props }) => {
22
22
  /**
@@ -75,7 +75,7 @@ loading, options, labelKey = 'label', primaryKey = 'value', childrenKey = 'child
75
75
  * --------------------------------------------------------------------
76
76
  * 控制选中状态
77
77
  */
78
- const [pathifiedValue, setPathifiedValue] = useControlled(defaultValue, value, onChange);
78
+ const [pathifiedValue, setPathifiedValue] = useControlled(defaultValue || [], value, onChange);
79
79
  // 路径转单一键
80
80
  const toStandardValue = (path) => {
81
81
  if (!path) {
@@ -11,7 +11,8 @@ import { SortableContext } from '@dnd-kit/sortable';
11
11
  import { isUnset, onDndDragEnd, useDndSensors } from '../../utils';
12
12
  import { Icon } from '../..';
13
13
  import { faGear } from '@fortawesome/free-solid-svg-icons/faGear';
14
- export const CurdColumnConfig = memo(({ columns = [], innerVisible, setInnerVisible, setInnerOrder, }) => {
14
+ export const CurdColumnConfig = memo(({ columns, innerVisible, setInnerVisible, setInnerOrder, }) => {
15
+ columns ||= [];
15
16
  const dragEndHandler = (e) => {
16
17
  const newColumns = onDndDragEnd(e, columns, '_key');
17
18
  newColumns && setInnerOrder(newColumns.flatMap(col => col._key ?? []));
@@ -14,7 +14,7 @@ const DataGridContext = createContext({});
14
14
  export function useDataGridContext() {
15
15
  return useContext(DataGridContext);
16
16
  }
17
- export const DataGrid = memo(({ columns, rows, rowProps, primaryKey = 'id', childrenKey = null, defaultOrderColumn, orderColumn, defaultOrderType = 'descend', orderType, onOrderChange, selectable, relation = 'dependent', integration = 'shallowest', allowSelectAll = true, clickRowToSelect = true, selectorProps, indent = 24, renderExpandIcon, defaultExpanded = [], expanded, onExpandedChange, paginatable = true, paginationProps = {}, renderPagination, loading, emptyPlaceholder, columnResizable = false, size, bordered, striped, tableProps, multiple, defaultValue, value, onChange, ...props }) => {
17
+ export const DataGrid = memo(({ columns, rows, rowProps, primaryKey = 'id', childrenKey = null, defaultOrderColumn, orderColumn, defaultOrderType = 'descend', orderType, onOrderChange, selectable, relation = 'dependent', integration = 'shallowest', allowSelectAll = true, clickRowToSelect = true, selectorProps, indent = 24, renderExpandIcon, defaultExpanded, expanded, onExpandedChange, paginatable = true, paginationProps, renderPagination, loading, emptyPlaceholder, columnResizable = false, size, bordered, striped, tableProps, multiple, defaultValue, value, onChange, ...props }) => {
18
18
  /**
19
19
  * ---------------------------------------------------------------
20
20
  * 选择行
@@ -61,7 +61,7 @@ export const DataGrid = memo(({ columns, rows, rowProps, primaryKey = 'id', chil
61
61
  * ---------------------------------------------------------------
62
62
  * 展开行
63
63
  */
64
- const [innerExpanded, setInnerExpanded] = useControlled(defaultExpanded, expanded);
64
+ const [innerExpanded, setInnerExpanded] = useControlled(defaultExpanded || [], expanded);
65
65
  const expandedSet = useMemo(() => {
66
66
  return new Set(innerExpanded.current);
67
67
  }, [innerExpanded.current]);
@@ -79,6 +79,7 @@ export const DataGrid = memo(({ columns, rows, rowProps, primaryKey = 'id', chil
79
79
  * ---------------------------------------------------------------
80
80
  * 分页
81
81
  */
82
+ paginationProps ||= {};
82
83
  const [innerPage, setInnerPage] = useControlled(paginationProps.defaultPage ?? 1, paginationProps.page, paginationProps.onPageChange);
83
84
  const [innerPageSize, setInnerPageSize] = useControlled(paginationProps.defaultPageSize ?? 10, paginationProps.pageSize, paginationProps.onPageSizeChange);
84
85
  const pageChangeHandler = (page, pageSize) => {
@@ -22,8 +22,8 @@ const commonControlProps = {
22
22
  color: 'text'
23
23
  };
24
24
  const bounceBezier = cubicBezier(0, 0, 0, 1);
25
- export const Gallery = memo(({ src = [], defaultIndex = 0, index, onIndexChange, defaultOpen = false, open, onOpenChange, showRotation = true, showZoom = true, showClose = true, renderControl, bounceElementTranslate = 24, bounceDragDistance = 240, effectiveSpeed = 450, ...props }) => {
26
- const srcArr = useSync(toArray(src));
25
+ export const Gallery = memo(({ src, defaultIndex = 0, index, onIndexChange, defaultOpen = false, open, onOpenChange, showRotation = true, showZoom = true, showClose = true, renderControl, bounceElementTranslate = 24, bounceDragDistance = 240, effectiveSpeed = 450, ...props }) => {
26
+ const srcArr = useSync(toArray(src || []));
27
27
  const [innerOpen, setInnerOpen] = useControlled(defaultOpen, open, onOpenChange);
28
28
  const close = () => {
29
29
  setInnerOpen(false);
@@ -12,10 +12,10 @@ export function useMenuContext() {
12
12
  const { size = theme.size, ellipsis = true, indent = theme.spacing[8], ...context } = useContext(MenuContext);
13
13
  return { size, ellipsis, indent, ...context };
14
14
  }
15
- export const Menu = memo(({ items, primaryKey = 'value', labelKey = 'label', childrenKey = 'children', defaultExpanded = [], expanded, onExpandedChange, multiple, defaultValue, value, onChange,
15
+ export const Menu = memo(({ items, primaryKey = 'value', labelKey = 'label', childrenKey = 'children', defaultExpanded, expanded, onExpandedChange, multiple, defaultValue, value, onChange,
16
16
  // 以下属性传递给<MenuItem/>
17
17
  size = 'large', showCheckbox, ellipsis, indent, ...props }) => {
18
- const [innerExpanded, setInnerExpanded] = useControlled(defaultExpanded, expanded);
18
+ const [innerExpanded, setInnerExpanded] = useControlled(defaultExpanded || [], expanded);
19
19
  const expandedSet = useMemo(() => {
20
20
  return new Set(innerExpanded.current);
21
21
  }, [innerExpanded.current]);
@@ -4,6 +4,8 @@ import { TransitionBaseProps } from '../transitionBase';
4
4
  export interface OverlayBaseProps extends DivProps {
5
5
  /** 模态的容器元素,默认为document.body */
6
6
  container?: DefineElement<HTMLElement>;
7
+ /** 同{@link container},但会在useEffect后取值,且只会执行一次 */
8
+ effectContainer?: DefineElement<HTMLElement>;
7
9
  /**
8
10
  * @enum {true} 跟随父组件强制渲染
9
11
  * @enum {false} 打开时渲染,关闭后销毁
@@ -22,4 +24,4 @@ export interface OverlayBaseProps extends DivProps {
22
24
  removeFocusOnOpen?: boolean;
23
25
  }
24
26
  export declare const overlayBaseTransitionDuration = 300;
25
- export declare function OverlayBase({ container, forceRender, open, onMaskClick, singleLayer, onOpened, onClosed, maskProps, removeFocusOnOpen, ...props }: OverlayBaseProps): false | React.ReactPortal;
27
+ export declare function OverlayBase({ container, effectContainer, forceRender, open, onMaskClick, singleLayer, onOpened, onClosed, maskProps, removeFocusOnOpen, ...props }: OverlayBaseProps): false | React.ReactPortal;
@@ -1,26 +1,26 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
2
2
  import { useEffect } from 'react';
3
3
  import { classes, style } from './overlayBase.style';
4
- import { clsx, useDerivedState } from '../../utils';
4
+ import { clsx, useContainer, useDerivedState } from '../../utils';
5
5
  import { createPortal } from 'react-dom';
6
6
  import { Fade } from '../transitionBase';
7
7
  export const overlayBaseTransitionDuration = 300;
8
- export function OverlayBase({ container, forceRender, open, onMaskClick, singleLayer, onOpened, onClosed, maskProps, removeFocusOnOpen = true, ...props }) {
8
+ export function OverlayBase({ container, effectContainer, forceRender, open, onMaskClick, singleLayer, onOpened, onClosed, maskProps, removeFocusOnOpen = true, ...props }) {
9
9
  const [shouldRender, setShouldRender] = useDerivedState((prev = false) => {
10
10
  if (open) {
11
11
  return true;
12
12
  }
13
13
  return forceRender === true || prev;
14
14
  }, [open, forceRender]);
15
- const containerEl = (typeof container === 'function' ? container() : container) || document.body;
15
+ const containerEl = useContainer(container, effectContainer);
16
16
  useEffect(() => {
17
17
  if (!open) {
18
18
  return;
19
19
  }
20
20
  removeFocusOnOpen && document.activeElement?.blur?.();
21
- containerEl.style.overflow = 'hidden';
21
+ containerEl.current.style.overflow = 'hidden';
22
22
  return () => {
23
- containerEl.style.overflow = '';
23
+ containerEl.current.style.overflow = '';
24
24
  };
25
25
  }, [open]);
26
26
  const clickHandler = (e) => {
@@ -36,5 +36,5 @@ export function OverlayBase({ container, forceRender, open, onMaskClick, singleL
36
36
  onClosed?.();
37
37
  forceRender === false && setShouldRender(false);
38
38
  };
39
- return shouldRender.current && createPortal(_jsxs("div", { ...props, css: style, className: clsx(classes.root, props.className), "data-open": open, "data-custom-container": containerEl !== document.body, children: [_jsx(Fade, { timeout: overlayBaseTransitionDuration, ...maskProps, in: open, className: clsx(classes.mask, maskProps?.className), onClick: clickHandler, onEntered: onEntered, onExited: onExited }), props.children] }), containerEl);
39
+ return shouldRender.current && createPortal(_jsxs("div", { ...props, css: style, className: clsx(classes.root, props.className), "data-open": open, "data-custom-container": containerEl.current !== document.body, children: [_jsx(Fade, { timeout: overlayBaseTransitionDuration, ...maskProps, in: open, className: clsx(classes.mask, maskProps?.className), onClick: clickHandler, onEntered: onEntered, onExited: onExited }), props.children] }), containerEl.current);
40
40
  }
@@ -7,6 +7,8 @@ export interface PopperProps extends Omit<DivProps, 'content' | 'children'> {
7
7
  anchorElement?: DefineElement<HTMLElement>;
8
8
  /** 弹框渲染的容器元素,默认为{@link document.body} */
9
9
  container?: DefineElement<HTMLElement>;
10
+ /** 同{@link container},但会在useEffect后取值,且只会执行一次 */
11
+ effectContainer?: DefineElement<HTMLElement>;
10
12
  /** 汽泡里的内容 */
11
13
  content?: ReactNode;
12
14
  /** 弹框偏离元素的距离 */
@@ -46,4 +48,4 @@ export interface PopperRef extends HTMLDivElement {
46
48
  openAnimation?: boolean;
47
49
  }, beforeOpen?: () => void): void;
48
50
  }
49
- export declare function Popper({ ref, popperRef, anchorElement, container, content, offset, trigger, placement, variant, sizeAdaptable, mouseEnterDelay, mouseLeaveDelay, defaultOpen, open, onOpenChange, onOpenChangeEnd, disabled, autoClose, forceRender, children, ...props }: PopperProps): import("@emotion/react/jsx-runtime").JSX.Element;
51
+ export declare function Popper({ ref, popperRef, anchorElement, container, effectContainer, content, offset, trigger, placement, variant, sizeAdaptable, mouseEnterDelay, mouseLeaveDelay, defaultOpen, open, onOpenChange, onOpenChangeEnd, disabled, autoClose, forceRender, children, ...props }: PopperProps): import("@emotion/react/jsx-runtime").JSX.Element;
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
2
2
  import { cloneElement, isValidElement, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from 'react';
3
3
  import { createPortal } from 'react-dom';
4
- import { clsx, cloneRef, isElementVisibleCompletely, listenAllPredecessorsScroll, toArray, useControlled, useDerivedState, useForceUpdate, useSync, useSyncState, useUnmounted } from '../../utils';
4
+ import { clsx, cloneRef, isElementVisibleCompletely, listenAllPredecessorsScroll, toArray, useControlled, useDerivedState, useForceUpdate, useSync, useSyncState, useUnmounted, useContainer } from '../../utils';
5
5
  import { ClickAway } from '../clickAway';
6
6
  import { classes, style } from './popper.style';
7
7
  import { PopperContext, usePopperContext } from './popperContext';
@@ -19,7 +19,7 @@ const getAttemptOrder = (placement) => {
19
19
  }
20
20
  return order;
21
21
  };
22
- export function Popper({ ref, popperRef, anchorElement, container = document.body, content, offset, trigger = 'hover', placement = 'top', variant = 'zoom', sizeAdaptable = variant === 'collapse', mouseEnterDelay = 100, mouseLeaveDelay = 150, defaultOpen = false, open, onOpenChange, onOpenChangeEnd, disabled, autoClose = false, forceRender, children, ...props }) {
22
+ export function Popper({ ref, popperRef, anchorElement, container = document.body, effectContainer, content, offset, trigger = 'hover', placement = 'top', variant = 'zoom', sizeAdaptable = variant === 'collapse', mouseEnterDelay = 100, mouseLeaveDelay = 150, defaultOpen = false, open, onOpenChange, onOpenChangeEnd, disabled, autoClose = false, forceRender, children, ...props }) {
23
23
  const { spacing } = useTheme();
24
24
  offset ??= spacing[2];
25
25
  useImperativeHandle(popperRef, () => {
@@ -101,14 +101,14 @@ export function Popper({ ref, popperRef, anchorElement, container = document.bod
101
101
  const getAnchorElement = () => {
102
102
  return anchorRef.current || (typeof syncAnchorElement.current === 'function' ? syncAnchorElement.current() : syncAnchorElement.current);
103
103
  };
104
- const containerRef = useSync((typeof container === 'function' ? container() : container) || document.body);
104
+ const containerEl = useContainer(container, effectContainer);
105
105
  const innerPopperRef = useRef(null);
106
106
  const [popperBounding, setPopperBounding] = useState();
107
107
  const [openNextFrame, setOpenNextFrame] = useDerivedState(!innerOpen.current, [innerOpen.current, contextMenuEvent.current]);
108
108
  const placeA = useRef(void 0);
109
109
  const placeB = useRef(void 0);
110
110
  const fitPosition = (options, beforeOpen) => {
111
- const containerRect = containerRef.current.getBoundingClientRect();
111
+ const containerRect = containerEl.current.getBoundingClientRect();
112
112
  const popperEl = innerPopperRef.current;
113
113
  let { offsetWidth: popperWidth, offsetHeight: popperHeight } = popperEl;
114
114
  let pA, pB;
@@ -184,7 +184,7 @@ export function Popper({ ref, popperRef, anchorElement, container = document.bod
184
184
  }
185
185
  popperEl.style.left = left + 'px';
186
186
  popperEl.style.top = top + 'px';
187
- return isElementVisibleCompletely(popperEl, containerRef.current === document.body ? void 0 : containerRef.current);
187
+ return isElementVisibleCompletely(popperEl, containerEl.current === document.body ? void 0 : containerEl.current);
188
188
  };
189
189
  }
190
190
  else {
@@ -255,7 +255,7 @@ export function Popper({ ref, popperRef, anchorElement, container = document.bod
255
255
  }
256
256
  popperEl.style.left = left + 'px';
257
257
  popperEl.style.top = top + 'px';
258
- return isElementVisibleCompletely(popperEl, containerRef.current === document.body ? void 0 : containerRef.current);
258
+ return isElementVisibleCompletely(popperEl, containerEl.current === document.body ? void 0 : containerEl.current);
259
259
  };
260
260
  }
261
261
  if (options?.forcePlacement) {
@@ -499,5 +499,5 @@ export function Popper({ ref, popperRef, anchorElement, container = document.bod
499
499
  transform: 'scale(1)'
500
500
  },
501
501
  ...props.style
502
- }, "data-open": innerOpen.current, "data-variant": variant, "data-place-a": placeA.current, "data-place-b": placeB.current, onTransitionEnd: onTransitionEnd, children: _jsx(PopperContext, { value: contextValue, children: content }) }) }), containerRef.current)] }));
502
+ }, "data-open": innerOpen.current, "data-variant": variant, "data-place-a": placeA.current, "data-place-b": placeB.current, onTransitionEnd: onTransitionEnd, children: _jsx(PopperContext, { value: contextValue, children: content }) }) }), containerEl.current)] }));
503
503
  }
@@ -7,7 +7,9 @@ import { css, keyframes } from '@emotion/react';
7
7
  import { Icon } from '../..';
8
8
  import { faCircleCheck } from '@fortawesome/free-solid-svg-icons/faCircleCheck';
9
9
  import { faCircleXmark } from '@fortawesome/free-solid-svg-icons/faCircleXmark';
10
- export const Progress = memo(({ showInfo = true, renderInfo, color = 'primary', status = 'default', variant = 'linear', gapDegree = 90, size = 60, indeterminate = false, barWidth = 4, strokeLinecap = variant === 'gauge' ? 'butt' : 'round', value = 0, ...props }) => {
10
+ export const Progress = memo(({ showInfo = true, renderInfo, color, status = 'default', variant = 'linear', gapDegree = 90, size = 60, indeterminate = false, barWidth = 4, strokeLinecap = variant === 'gauge' ? 'butt' : 'round', value = 0, ...props }) => {
11
+ const isColorSpecified = !!color;
12
+ color ??= 'primary';
11
13
  const { colors: { success, error } } = useTheme();
12
14
  const isSucceed = status === 'success' || (status !== 'error' && value === 100 && variant !== 'gauge');
13
15
  const renderInfoFn = () => {
@@ -52,7 +54,7 @@ export const Progress = memo(({ showInfo = true, renderInfo, color = 'primary',
52
54
  }
53
55
  return;
54
56
  }, [indeterminate, variant, strokeLinecap, size, barWidth]);
55
- return (_jsx("div", { ...props, css: useStyle({ color: color || 'primary', variant }), className: clsx(classes.root, props.className), "data-variant": variant, "data-indeterminate": indeterminate, "data-processing": value < 100 && status === 'processing', "data-success": isSucceed, "data-error": status === 'error', children: variant === 'linear'
57
+ return (_jsx("div", { ...props, css: useStyle({ color: color || 'primary', variant }), className: clsx(classes.root, props.className), "data-variant": variant, "data-indeterminate": indeterminate, "data-processing": value < 100 && status === 'processing', "data-success": isColorSpecified ? void 0 : isSucceed, "data-error": isColorSpecified ? void 0 : status === 'error', children: variant === 'linear'
56
58
  ? _jsxs(_Fragment, { children: [_jsx("div", { className: classes.track, style: {
57
59
  height: barWidth,
58
60
  ...props.style
@@ -24,11 +24,12 @@ export declare class SnackbarBaseMethods<P extends SnackbarBaseProps = SnackbarB
24
24
  error(content: ReactNode): Promise<void>;
25
25
  error(props: P): Promise<void>;
26
26
  }
27
- export declare const SnackbarBase: React.MemoExoticComponent<({ methods, useTo, max, container }: {
27
+ export declare const SnackbarBase: React.MemoExoticComponent<({ methods, useTo, max, container, effectContainer }: {
28
28
  methods: SnackbarBaseMethods;
29
29
  useTo: "message" | "notification";
30
30
  max?: number;
31
- container?: DefineElement;
31
+ container?: DefineElement<HTMLElement>;
32
+ effectContainer?: DefineElement<HTMLElement>;
32
33
  }) => React.ReactPortal>;
33
34
  interface SnackbarBaseItemProps extends Omit<SnackbarBaseProps, 'duration' | 'onAutoClose'> {
34
35
  id: string;
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
2
2
  import { createElement as _createElement } from "@emotion/react";
3
3
  import { isValidElement, memo, useEffect, useRef, useState } from 'react';
4
4
  import { Collapse, Slide } from '../transitionBase';
5
- import { clsx, getEasyID, useColor } from '../../utils';
5
+ import { clsx, getEasyID, useColor, useContainer } from '../../utils';
6
6
  import { classes, style } from './snackbarBase.style';
7
7
  import { TransitionGroup } from 'react-transition-group';
8
8
  import { StatusIcon, statusMapToIconDefinition } from '../status';
@@ -33,7 +33,7 @@ const placementToIndex = {
33
33
  bottomRight: 5,
34
34
  rightBottom: 5
35
35
  };
36
- export const SnackbarBase = memo(({ methods, useTo, max = useTo === 'message' ? 5 : 4, container }) => {
36
+ export const SnackbarBase = memo(({ methods, useTo, max = useTo === 'message' ? 5 : 4, container, effectContainer }) => {
37
37
  const [stacks, setStacks] = useState([]);
38
38
  const timers = useRef([]);
39
39
  useEffect(() => () => {
@@ -81,10 +81,10 @@ export const SnackbarBase = memo(({ methods, useTo, max = useTo === 'message' ?
81
81
  methods.warning = defineMethod('warning');
82
82
  methods.error = defineMethod('error');
83
83
  const css = style();
84
- const containerEl = (typeof container === 'function' ? container() : container) || document.body;
84
+ const containerEl = useContainer(container, effectContainer);
85
85
  return createPortal(stacks.flatMap((stack, i) => stack
86
86
  ? _jsx(TransitionGroup, { css: css, className: classes.root, "data-place": i, "data-use-to": useTo, children: stack.map(p => _createElement(SnackbarBaseItem, { ...p, key: p.id })) }, i)
87
- : []), containerEl);
87
+ : []), containerEl.current);
88
88
  });
89
89
  /**
90
90
  * ----------------------------------------------------------------------
@@ -15,6 +15,7 @@ export declare function restoreThemeDefinition(theme: PartialTheme): ThemeDefini
15
15
  * 统一全局zIndex
16
16
  */
17
17
  export declare const zIndex: {
18
+ touchRipple: number;
18
19
  overlay: number;
19
20
  popper: number;
20
21
  dropdown: number;
@@ -91,6 +91,7 @@ export function restoreThemeDefinition(theme) {
91
91
  * 统一全局zIndex
92
92
  */
93
93
  export const zIndex = {
94
+ touchRipple: 900,
94
95
  overlay: 1000,
95
96
  popper: 1100,
96
97
  dropdown: 1200,
@@ -1 +1,2 @@
1
1
  export * from './touchRipple';
2
+ export * from './touchRipple';
@@ -1 +1,2 @@
1
1
  export * from './touchRipple';
2
+ export * from './touchRipple';
@@ -1,16 +1,8 @@
1
- import { ReactElement } from 'react';
2
- import { ColorPropsValue, DivProps } from '../../types';
3
- export interface TouchRippleProps extends Omit<DivProps, 'children'> {
1
+ import { ColorPropsValue, DefineElement, DivProps } from '../../types';
2
+ export interface TouchRippleOverlayProps extends DivProps {
4
3
  color?: ColorPropsValue;
5
- rippleProps?: DivProps;
6
- children?: ReactElement;
4
+ /** 容器元素,默认为该组件元素的父元素 */
5
+ container?: DefineElement<HTMLElement>;
6
+ effectContainer?: DefineElement<HTMLElement>;
7
7
  }
8
- export declare const TouchRipple: {
9
- (props: TouchRippleProps): ReactElement;
10
- Ripple: typeof Ripple;
11
- };
12
- interface RippleProps extends DivProps {
13
- color?: ColorPropsValue;
14
- }
15
- export declare const Ripple: import("react").MemoExoticComponent<({ ref, color, ...props }: RippleProps) => import("@emotion/react/jsx-runtime").JSX.Element>;
16
- export {};
8
+ export declare const TouchRipple: import("react").MemoExoticComponent<({ color, container, effectContainer, ...props }: TouchRippleOverlayProps) => import("@emotion/react/jsx-runtime").JSX.Element>;
@@ -1,54 +1,69 @@
1
1
  import { jsx as _jsx } from "@emotion/react/jsx-runtime";
2
- import { cloneElement, memo, useRef, useState, useLayoutEffect } from 'react';
2
+ import { memo, useEffect, useRef, useState } from 'react';
3
+ import { createPortal } from 'react-dom';
4
+ import { cloneRef, clsx, useContainer } from '../../utils';
3
5
  import { classes, useStyle } from './touchRipple.style';
4
- import { cloneRef, getEasyID } from '../../utils';
5
- export const TouchRipple = (({ ref, color, rippleProps, children, ...props }) => {
6
- const renderedRipple = (_jsx(Ripple, { ...rippleProps,
7
- // 有children属性时,ref传递给children
8
- ref: children ? ref : void 0 }));
9
- return children
10
- // 有children属性时,props传递给children,再追加Ripple组件
11
- ? cloneElement(children, { ...props, ref }, children.props.children, renderedRipple)
12
- : renderedRipple;
13
- });
14
- export const Ripple = memo(({ ref, color = 'primary', ...props }) => {
15
- const [ripples, setRipples] = useState([]);
16
- const rippleRef = useRef(null);
17
- useLayoutEffect(() => {
18
- const wrapper = rippleRef.current?.parentElement;
19
- if (wrapper) {
20
- const pointerDown = (e) => {
21
- setRipples(o => [
22
- ...o,
23
- {
24
- key: getEasyID('touch-ripple'),
25
- r: Math.sqrt(wrapper.clientWidth ** 2 + wrapper.clientHeight ** 2),
26
- left: e.offsetX,
27
- top: e.offsetY,
6
+ export const TouchRipple = memo(({ color = 'primary', container, effectContainer, ...props }) => {
7
+ const ref = useRef(null);
8
+ const [ripples, setRipples] = useState(new Map());
9
+ const incrementKey = useRef(0);
10
+ const containerEl = useContainer(container, effectContainer, null);
11
+ useEffect(() => {
12
+ const parentElement = containerEl.current ?? ref.current.parentElement;
13
+ if (parentElement) {
14
+ const pointerDown = ({ offsetX, offsetY }) => {
15
+ const maxWidth = Math.max(parentElement.clientWidth - offsetX, offsetX);
16
+ const maxHeight = Math.max(parentElement.clientHeight - offsetY, offsetY);
17
+ setRipples(o => {
18
+ const r = new Map(o);
19
+ const key = ++incrementKey.current;
20
+ r.set(key, {
21
+ key,
22
+ diameter: Math.sqrt(maxWidth ** 2 + maxHeight ** 2) * 2,
23
+ left: offsetX,
24
+ top: offsetY,
28
25
  leaving: false
29
- }
30
- ]);
31
- wrapper.addEventListener('pointerup', leave);
32
- wrapper.addEventListener('pointerleave', leave);
26
+ });
27
+ return r;
28
+ });
29
+ parentElement.addEventListener('pointerup', leave);
30
+ parentElement.addEventListener('pointerleave', leave);
33
31
  };
34
32
  const leave = () => {
35
- setRipples(o => o.map(v => ({ ...v, leaving: true })));
33
+ setRipples(o => {
34
+ const r = new Map(o);
35
+ r.forEach(v => v.leaving = true);
36
+ return r;
37
+ });
36
38
  removeLeaveListener();
37
39
  };
38
- wrapper.addEventListener('pointerdown', pointerDown);
39
40
  const removeLeaveListener = () => {
40
- wrapper.removeEventListener('pointerup', leave);
41
- wrapper.removeEventListener('pointerleave', leave);
41
+ parentElement.removeEventListener('pointerup', leave);
42
+ parentElement.removeEventListener('pointerleave', leave);
42
43
  };
44
+ parentElement.addEventListener('pointerdown', pointerDown);
43
45
  return () => {
44
- wrapper.removeEventListener('pointerdown', pointerDown);
46
+ parentElement.removeEventListener('pointerdown', pointerDown);
45
47
  removeLeaveListener();
46
48
  };
47
49
  }
48
- return;
49
- }, []);
50
- const onTransitionEnd = (key) => {
51
- setRipples(o => o.filter(v => v.key !== key));
50
+ }, [containerEl.current]);
51
+ const leftHandler = (key) => {
52
+ setRipples(o => {
53
+ const r = new Map(o);
54
+ r.delete(key);
55
+ if (!r.size) {
56
+ incrementKey.current = 0;
57
+ }
58
+ return r;
59
+ });
52
60
  };
53
- return (_jsx("div", { ...props, ref: cloneRef(ref, rippleRef), css: useStyle({ color: color || 'primary' }), className: classes.root, children: ripples.map(({ key, r, left, top, leaving }) => _jsx("div", { className: classes.ripple, style: { width: r * 2, left, top }, "data-leaving": leaving, onTransitionEnd: () => onTransitionEnd(key) }, key)) }));
61
+ const children = (_jsx("div", { ...props, ref: cloneRef(ref, props.ref), css: useStyle({ color }), className: clsx(classes.root, props.className), children: ripples.values().toArray().map(({ key, diameter, left, top, leaving }) => _jsx("div", { className: classes.ripple, style: {
62
+ width: diameter,
63
+ height: diameter,
64
+ left, top
65
+ }, "data-leaving": leaving, onTransitionEnd: () => leftHandler(key) }, key)) }));
66
+ return containerEl.current
67
+ ? createPortal(children, containerEl.current)
68
+ : children;
54
69
  });