@canlooks/can-ui 0.0.112 → 0.0.113

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.
@@ -27,12 +27,12 @@ exports.App = (({ theme, ...props }) => {
27
27
  return ((0, jsx_runtime_1.jsx)(globalEventDelegation_1.GlobalEventDelegation, { children: (0, jsx_runtime_1.jsx)(theme_1.ThemeProvider, { theme: theme, children: (0, jsx_runtime_1.jsx)(InnerApp, { ...props }) }) }));
28
28
  });
29
29
  function InnerApp({ component: Component = 'div', theme, children, fill = true, ...props }) {
30
- const appValue = (0, react_1.useMemo)(() => ({
30
+ const appValue = (0, utils_1.useExternalClass)(() => ({
31
31
  dialog: exports.App.dialog = new appDialog_1.AppDialogMethods(),
32
32
  message: exports.App.message = new appMessage_1.AppMessageMethods(),
33
33
  notification: exports.App.notification = new appNotification_1.AppNotificationMethods(),
34
34
  actionSheet: exports.App.actionSheet = new appActionSheet_1.AppActionSheetMethods()
35
- }), []);
35
+ }));
36
36
  return ((0, jsx_runtime_1.jsxs)(AppContext, { value: appValue, children: [Component
37
37
  ? (0, jsx_runtime_1.jsx)(Component, { ...props, css: [
38
38
  app_style_1.style,
@@ -24,4 +24,4 @@ export interface OverlayBaseProps extends DivProps {
24
24
  removeFocusOnOpen?: boolean;
25
25
  }
26
26
  export declare const overlayBaseTransitionDuration = 300;
27
- export declare function OverlayBase({ container, effectContainer, 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 | null;
@@ -37,7 +37,7 @@ function OverlayBase({ container, effectContainer, forceRender, open, onMaskClic
37
37
  onClosed?.();
38
38
  forceRender === false && setShouldRender(false);
39
39
  };
40
- return shouldRender.current && (0, react_dom_1.createPortal)((0, jsx_runtime_1.jsxs)("div", { ...props, css: overlayBase_style_1.style, className: (0, utils_1.clsx)(overlayBase_style_1.classes.root, props.className), "data-open": open, "data-custom-container": containerEl.current !== document.body, children: [(0, jsx_runtime_1.jsx)(transitionBase_1.Fade, { timeout: exports.overlayBaseTransitionDuration, ...(0, utils_1.mergeComponentProps)(maskProps, {
40
+ return shouldRender.current && containerEl.current && (0, react_dom_1.createPortal)((0, jsx_runtime_1.jsxs)("div", { ...props, css: overlayBase_style_1.style, className: (0, utils_1.clsx)(overlayBase_style_1.classes.root, props.className), "data-open": open, "data-custom-container": containerEl.current !== document.body, children: [(0, jsx_runtime_1.jsx)(transitionBase_1.Fade, { timeout: exports.overlayBaseTransitionDuration, ...(0, utils_1.mergeComponentProps)(maskProps, {
41
41
  in: open,
42
42
  className: overlayBase_style_1.classes.mask,
43
43
  onClick,
@@ -537,7 +537,7 @@ function Popper({ ref, popperRef, anchorElement, container = document.body, effe
537
537
  ? (0, react_1.cloneElement)(children, {
538
538
  ref: childRef
539
539
  })
540
- : children, renderedOnce.current && (0, react_dom_1.createPortal)((0, jsx_runtime_1.jsx)(clickAway_1.ClickAway, { disabled: !clickable && !enterable && !contextMenuable,
540
+ : children, renderedOnce.current && containerEl.current && (0, react_dom_1.createPortal)((0, jsx_runtime_1.jsx)(clickAway_1.ClickAway, { disabled: !clickable && !enterable && !contextMenuable,
541
541
  // 右键菜单点击anchor需要关闭弹框
542
542
  targets: () => contextMenuEvent.current ? void 0 : getAnchorElement(), onClickAway: onClickAway, children: (0, jsx_runtime_1.jsx)("div", { ...props, ref: innerPopperRef, css: popper_style_1.style, className: (0, utils_1.clsx)(popper_style_1.classes.root, props.className), style: {
543
543
  ...popperBounding,
@@ -30,7 +30,7 @@ export declare const SnackbarBase: React.MemoExoticComponent<({ methods, useTo,
30
30
  max?: number;
31
31
  container?: DefineElement<HTMLElement>;
32
32
  effectContainer?: DefineElement<HTMLElement>;
33
- }) => React.ReactPortal>;
33
+ }) => React.ReactPortal | null>;
34
34
  interface SnackbarBaseItemProps extends Omit<SnackbarBaseProps, 'duration' | 'onAutoClose'> {
35
35
  id: string;
36
36
  type: keyof SnackbarBaseMethods;
@@ -86,7 +86,7 @@ exports.SnackbarBase = (0, react_2.memo)(({ methods, useTo, max = useTo === 'mes
86
86
  methods.error = defineMethod('error');
87
87
  const css = (0, snackbarBase_style_1.style)();
88
88
  const containerEl = (0, utils_1.useContainer)(container, effectContainer);
89
- return (0, react_dom_1.createPortal)(stacks.flatMap((stack, i) => stack
89
+ return containerEl.current && (0, react_dom_1.createPortal)(stacks.flatMap((stack, i) => stack
90
90
  ? (0, jsx_runtime_1.jsx)(react_transition_group_1.TransitionGroup, { css: css, className: snackbarBase_style_1.classes.root, "data-place": i, "data-use-to": useTo, children: stack.map(p => (0, react_1.createElement)(exports.SnackbarBaseItem, { ...p, key: p.id })) }, i)
91
91
  : []), containerEl.current);
92
92
  });
@@ -55,4 +55,8 @@ export declare function useLoading<A extends any[], R>(fn: (...args: A) => R | P
55
55
  * @param effectContainer
56
56
  * @param defaultContainer 默认为`document.body`
57
57
  */
58
- export declare function useContainer<T extends HTMLElement | null>(container?: DefineElement<T>, effectContainer?: DefineElement<T>, defaultContainer?: T): RefObject<T>;
58
+ export declare function useContainer<T extends HTMLElement | null>(container?: DefineElement<T>, effectContainer?: DefineElement<T>, defaultContainer?: DefineElement<T>): RefObject<T | null>;
59
+ /**
60
+ * 使用外部类,该方法可避免`StrictMode`下,React渲染行为与外部类实例生命周期不同步的问题
61
+ */
62
+ export declare function useExternalClass<T>(setup: () => T, cleanup?: (instance: T) => void): T;
@@ -9,6 +9,7 @@ exports.useUnmounted = useUnmounted;
9
9
  exports.useControlled = useControlled;
10
10
  exports.useLoading = useLoading;
11
11
  exports.useContainer = useContainer;
12
+ exports.useExternalClass = useExternalClass;
12
13
  const react_1 = require("react");
13
14
  const utils_1 = require("./utils");
14
15
  /**
@@ -130,18 +131,36 @@ function useLoading(fn, referredLoading = false) {
130
131
  * @param effectContainer
131
132
  * @param defaultContainer 默认为`document.body`
132
133
  */
133
- function useContainer(container, effectContainer, defaultContainer = document.body) {
134
+ function useContainer(container, effectContainer, defaultContainer) {
134
135
  const [containerEl, setContainerEl] = useDerivedState(prev => {
135
136
  if (container) {
136
137
  return typeof container === 'function' ? container() : container;
137
138
  }
138
- return prev || defaultContainer;
139
- }, [container, defaultContainer]);
139
+ return prev || null;
140
+ }, [container]);
140
141
  (0, react_1.useEffect)(() => {
141
- if (effectContainer) {
142
- const el = typeof effectContainer === 'function' ? effectContainer() : effectContainer;
142
+ const _container = effectContainer || defaultContainer || document.body;
143
+ if (_container) {
144
+ const el = typeof _container === 'function' ? _container() : _container;
143
145
  setContainerEl(el);
144
146
  }
145
- }, []);
147
+ }, [effectContainer, defaultContainer]);
146
148
  return containerEl;
147
149
  }
150
+ /**
151
+ * 使用外部类,该方法可避免`StrictMode`下,React渲染行为与外部类实例生命周期不同步的问题
152
+ */
153
+ function useExternalClass(setup, cleanup) {
154
+ const mountTimes = (0, react_1.useRef)(0);
155
+ const prevInstance = (0, react_1.useRef)(void 0);
156
+ const [instance] = (0, react_1.useState)(() => {
157
+ if (!mountTimes.current++) {
158
+ prevInstance.current = setup();
159
+ }
160
+ return prevInstance.current;
161
+ });
162
+ (0, react_1.useEffect)(() => () => {
163
+ !--mountTimes.current && cleanup?.(instance);
164
+ }, []);
165
+ return instance;
166
+ }
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
2
- import { createContext, useContext, useMemo } from 'react';
3
- import { clsx, defineCss } from '../../utils';
2
+ import { createContext, useContext } from 'react';
3
+ import { clsx, defineCss, useExternalClass } from '../../utils';
4
4
  import { classes, style } from './app.style';
5
5
  import { ThemeProvider } from '../theme';
6
6
  import { AppDialog, AppDialogMethods } from './appDialog';
@@ -22,12 +22,12 @@ export const App = (({ theme, ...props }) => {
22
22
  return (_jsx(GlobalEventDelegation, { children: _jsx(ThemeProvider, { theme: theme, children: _jsx(InnerApp, { ...props }) }) }));
23
23
  });
24
24
  export function InnerApp({ component: Component = 'div', theme, children, fill = true, ...props }) {
25
- const appValue = useMemo(() => ({
25
+ const appValue = useExternalClass(() => ({
26
26
  dialog: App.dialog = new AppDialogMethods(),
27
27
  message: App.message = new AppMessageMethods(),
28
28
  notification: App.notification = new AppNotificationMethods(),
29
29
  actionSheet: App.actionSheet = new AppActionSheetMethods()
30
- }), []);
30
+ }));
31
31
  return (_jsxs(AppContext, { value: appValue, children: [Component
32
32
  ? _jsx(Component, { ...props, css: [
33
33
  style,
@@ -24,4 +24,4 @@ export interface OverlayBaseProps extends DivProps {
24
24
  removeFocusOnOpen?: boolean;
25
25
  }
26
26
  export declare const overlayBaseTransitionDuration = 300;
27
- export declare function OverlayBase({ container, effectContainer, 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 | null;
@@ -33,7 +33,7 @@ export function OverlayBase({ container, effectContainer, forceRender, open, onM
33
33
  onClosed?.();
34
34
  forceRender === false && setShouldRender(false);
35
35
  };
36
- 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, ...mergeComponentProps(maskProps, {
36
+ return shouldRender.current && containerEl.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, ...mergeComponentProps(maskProps, {
37
37
  in: open,
38
38
  className: classes.mask,
39
39
  onClick,
@@ -534,7 +534,7 @@ export function Popper({ ref, popperRef, anchorElement, container = document.bod
534
534
  ? cloneElement(children, {
535
535
  ref: childRef
536
536
  })
537
- : children, renderedOnce.current && createPortal(_jsx(ClickAway, { disabled: !clickable && !enterable && !contextMenuable,
537
+ : children, renderedOnce.current && containerEl.current && createPortal(_jsx(ClickAway, { disabled: !clickable && !enterable && !contextMenuable,
538
538
  // 右键菜单点击anchor需要关闭弹框
539
539
  targets: () => contextMenuEvent.current ? void 0 : getAnchorElement(), onClickAway: onClickAway, children: _jsx("div", { ...props, ref: innerPopperRef, css: style, className: clsx(classes.root, props.className), style: {
540
540
  ...popperBounding,
@@ -30,7 +30,7 @@ export declare const SnackbarBase: React.MemoExoticComponent<({ methods, useTo,
30
30
  max?: number;
31
31
  container?: DefineElement<HTMLElement>;
32
32
  effectContainer?: DefineElement<HTMLElement>;
33
- }) => React.ReactPortal>;
33
+ }) => React.ReactPortal | null>;
34
34
  interface SnackbarBaseItemProps extends Omit<SnackbarBaseProps, 'duration' | 'onAutoClose'> {
35
35
  id: string;
36
36
  type: keyof SnackbarBaseMethods;
@@ -82,7 +82,7 @@ export const SnackbarBase = memo(({ methods, useTo, max = useTo === 'message' ?
82
82
  methods.error = defineMethod('error');
83
83
  const css = style();
84
84
  const containerEl = useContainer(container, effectContainer);
85
- return createPortal(stacks.flatMap((stack, i) => stack
85
+ return containerEl.current && 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
87
  : []), containerEl.current);
88
88
  });
@@ -55,4 +55,8 @@ export declare function useLoading<A extends any[], R>(fn: (...args: A) => R | P
55
55
  * @param effectContainer
56
56
  * @param defaultContainer 默认为`document.body`
57
57
  */
58
- export declare function useContainer<T extends HTMLElement | null>(container?: DefineElement<T>, effectContainer?: DefineElement<T>, defaultContainer?: T): RefObject<T>;
58
+ export declare function useContainer<T extends HTMLElement | null>(container?: DefineElement<T>, effectContainer?: DefineElement<T>, defaultContainer?: DefineElement<T>): RefObject<T | null>;
59
+ /**
60
+ * 使用外部类,该方法可避免`StrictMode`下,React渲染行为与外部类实例生命周期不同步的问题
61
+ */
62
+ export declare function useExternalClass<T>(setup: () => T, cleanup?: (instance: T) => void): T;
@@ -119,18 +119,36 @@ export function useLoading(fn, referredLoading = false) {
119
119
  * @param effectContainer
120
120
  * @param defaultContainer 默认为`document.body`
121
121
  */
122
- export function useContainer(container, effectContainer, defaultContainer = document.body) {
122
+ export function useContainer(container, effectContainer, defaultContainer) {
123
123
  const [containerEl, setContainerEl] = useDerivedState(prev => {
124
124
  if (container) {
125
125
  return typeof container === 'function' ? container() : container;
126
126
  }
127
- return prev || defaultContainer;
128
- }, [container, defaultContainer]);
127
+ return prev || null;
128
+ }, [container]);
129
129
  useEffect(() => {
130
- if (effectContainer) {
131
- const el = typeof effectContainer === 'function' ? effectContainer() : effectContainer;
130
+ const _container = effectContainer || defaultContainer || document.body;
131
+ if (_container) {
132
+ const el = typeof _container === 'function' ? _container() : _container;
132
133
  setContainerEl(el);
133
134
  }
134
- }, []);
135
+ }, [effectContainer, defaultContainer]);
135
136
  return containerEl;
136
137
  }
138
+ /**
139
+ * 使用外部类,该方法可避免`StrictMode`下,React渲染行为与外部类实例生命周期不同步的问题
140
+ */
141
+ export function useExternalClass(setup, cleanup) {
142
+ const mountTimes = useRef(0);
143
+ const prevInstance = useRef(void 0);
144
+ const [instance] = useState(() => {
145
+ if (!mountTimes.current++) {
146
+ prevInstance.current = setup();
147
+ }
148
+ return prevInstance.current;
149
+ });
150
+ useEffect(() => () => {
151
+ !--mountTimes.current && cleanup?.(instance);
152
+ }, []);
153
+ return instance;
154
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canlooks/can-ui",
3
- "version": "0.0.112",
3
+ "version": "0.0.113",
4
4
  "author": "C.CanLiang <canlooks@gmail.com>",
5
5
  "description": "My ui framework",
6
6
  "license": "MIT",