clxx 2.1.2 → 2.1.4

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 (89) hide show
  1. package/LICENSE +20 -20
  2. package/README.md +768 -35
  3. package/build/Ago/index.d.ts +0 -1
  4. package/build/Alert/Wrapper.d.ts +0 -1
  5. package/build/Alert/Wrapper.js +12 -12
  6. package/build/Alert/index.js +2 -2
  7. package/build/AutoGrid/index.js +16 -21
  8. package/build/AutoGrid/style.d.ts +5 -5
  9. package/build/CarouselNotice/index.d.ts +0 -1
  10. package/build/CarouselNotice/index.js +6 -6
  11. package/build/CarouselNotice/style.d.ts +6 -1
  12. package/build/CarouselNotice/style.js +7 -7
  13. package/build/Clickable/index.d.ts +5 -5
  14. package/build/Clickable/index.js +11 -8
  15. package/build/Container/index.d.ts +2 -2
  16. package/build/Container/index.js +85 -60
  17. package/build/Countdowner/index.d.ts +3 -3
  18. package/build/Countdowner/index.js +18 -11
  19. package/build/Dialog/Wrapper.d.ts +0 -1
  20. package/build/Dialog/Wrapper.js +7 -10
  21. package/build/Dialog/style.d.ts +15 -10
  22. package/build/Dialog/style.js +88 -88
  23. package/build/Effect/useInterval.d.ts +1 -1
  24. package/build/Effect/useInterval.js +1 -1
  25. package/build/Effect/useUpdate.d.ts +1 -1
  26. package/build/Effect/useUpdate.js +1 -1
  27. package/build/Effect/useWindowResize.d.ts +4 -2
  28. package/build/Effect/useWindowResize.js +28 -11
  29. package/build/Flex/index.d.ts +0 -1
  30. package/build/Indicator/index.js +22 -23
  31. package/build/Indicator/style.d.ts +6 -1
  32. package/build/Indicator/style.js +7 -7
  33. package/build/Loading/Wrapper.js +2 -2
  34. package/build/Loading/style.d.ts +12 -2
  35. package/build/Loading/style.js +14 -14
  36. package/build/Overlay/index.d.ts +1 -1
  37. package/build/Overlay/index.js +41 -37
  38. package/build/SafeArea/index.d.ts +1 -2
  39. package/build/SafeArea/index.js +6 -6
  40. package/build/ScrollView/index.d.ts +1 -1
  41. package/build/ScrollView/index.js +111 -27
  42. package/build/Toast/Toast.js +4 -4
  43. package/build/Toast/style.d.ts +42 -12
  44. package/build/Toast/style.js +54 -54
  45. package/build/index.d.ts +3 -0
  46. package/build/index.js +3 -0
  47. package/build/utils/Countdown.d.ts +3 -1
  48. package/build/utils/Countdown.js +5 -2
  49. package/build/utils/createApp.d.ts +14 -13
  50. package/build/utils/createApp.js +58 -42
  51. package/build/utils/cssUtil.d.ts +2 -2
  52. package/build/utils/cssUtil.js +24 -13
  53. package/build/utils/defaultScroll.d.ts +0 -1
  54. package/build/utils/defaultScroll.js +8 -5
  55. package/build/utils/is.d.ts +23 -4
  56. package/build/utils/is.js +97 -15
  57. package/build/utils/jsonp.js +2 -2
  58. package/build/utils/request.js +12 -2
  59. package/package.json +15 -12
  60. package/test/README.md +16 -0
  61. package/test/eslint.config.js +29 -0
  62. package/test/index.html +13 -0
  63. package/test/jsconfig.json +8 -0
  64. package/test/package.json +27 -0
  65. package/test/public/vite.svg +1 -0
  66. package/test/src/ago/index.jsx +30 -0
  67. package/test/src/alert/index.jsx +111 -0
  68. package/test/src/autogrid/index.css +0 -0
  69. package/test/src/autogrid/index.jsx +26 -0
  70. package/test/src/carouse-notice/index.jsx +59 -0
  71. package/test/src/clickable/index.css +21 -0
  72. package/test/src/clickable/index.jsx +39 -0
  73. package/test/src/countdown/index.jsx +95 -0
  74. package/test/src/dialog/index.jsx +104 -0
  75. package/test/src/dialog/index.module.css +5 -0
  76. package/test/src/image-picker/index.css +0 -0
  77. package/test/src/image-picker/index.jsx +88 -0
  78. package/test/src/index/index.jsx +46 -0
  79. package/test/src/index.css +49 -0
  80. package/test/src/index.jsx +31 -0
  81. package/test/src/indicator/index.jsx +25 -0
  82. package/test/src/loading/index.jsx +36 -0
  83. package/test/src/overlay/index.jsx +31 -0
  84. package/test/src/privacy/index.css +13 -0
  85. package/test/src/privacy/index.jsx +34 -0
  86. package/test/src/scrollview/index.css +10 -0
  87. package/test/src/scrollview/index.jsx +52 -0
  88. package/test/src/toast/index.jsx +86 -0
  89. package/test/vite.config.js +15 -0
@@ -10,12 +10,12 @@ var __rest = (this && this.__rest) || function (s, e) {
10
10
  return t;
11
11
  };
12
12
  import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
13
- import { useEffect, useRef, useState } from "react";
13
+ import { useCallback, useLayoutEffect, useRef, useState } from "react";
14
14
  import { Indicator } from "../Indicator";
15
15
  import { RowCenter } from "../Flex/Row";
16
16
  import { style } from "./style";
17
17
  export function ScrollView(props) {
18
- const { children, height, reachTopThreshold = 50, onReachTop, reachBottomThreshold = 50, onReachBottom, showLoading = true, loadingContent, onScroll, containerStyle, wrapperStyle, loadingStyle } = props, attrs = __rest(props, ["children", "height", "reachTopThreshold", "onReachTop", "reachBottomThreshold", "onReachBottom", "showLoading", "loadingContent", "onScroll", "containerStyle", "wrapperStyle", "loadingStyle"]);
18
+ const { children, height, reachTopThreshold = 50, onReachTop, reachBottomThreshold = 50, onReachBottom, showLoading = true, loadingContent, onScroll, scrollThrottle = 16, containerStyle, wrapperStyle, loadingStyle } = props, attrs = __rest(props, ["children", "height", "reachTopThreshold", "onReachTop", "reachBottomThreshold", "onReachBottom", "showLoading", "loadingContent", "onScroll", "scrollThrottle", "containerStyle", "wrapperStyle", "loadingStyle"]);
19
19
  // 容器高度
20
20
  const heightStyle = {};
21
21
  if (height) {
@@ -23,58 +23,142 @@ export function ScrollView(props) {
23
23
  }
24
24
  // 滚动容器
25
25
  const container = useRef(null);
26
- const child = useRef(null);
27
26
  // 当前滚动到顶部的距离
28
- const top = useRef(0);
29
- const scrollCallback = (rawEvent) => {
30
- var _a, _b;
27
+ const lastScrollTop = useRef(0);
28
+ // 防止重复触发的标记
29
+ const hasReachedTop = useRef(false);
30
+ const hasReachedBottom = useRef(false);
31
+ // 节流控制
32
+ const throttleTimer = useRef(undefined);
33
+ const lastCallTime = useRef(0);
34
+ // 使用 ref 保存最新的回调函数,避免闭包陈旧
35
+ const callbacksRef = useRef({
36
+ onScroll,
37
+ onReachTop,
38
+ onReachBottom,
39
+ });
40
+ // 每次渲染都更新 ref 中的回调
41
+ callbacksRef.current = {
42
+ onScroll,
43
+ onReachTop,
44
+ onReachBottom,
45
+ };
46
+ // container是否有滚动条
47
+ const [hasScrollBar, setHasScrollBar] = useState(false);
48
+ // 检查是否有滚动条
49
+ const checkScrollBar = useCallback(() => {
50
+ if (container.current) {
51
+ const hasScroll = container.current.scrollHeight > container.current.clientHeight;
52
+ setHasScrollBar(hasScroll);
53
+ }
54
+ }, []);
55
+ // 使用 ResizeObserver 监听内容高度变化
56
+ useLayoutEffect(() => {
57
+ const containerEl = container.current;
58
+ if (!containerEl)
59
+ return;
60
+ // 初始检查
61
+ checkScrollBar();
62
+ // 使用 ResizeObserver 监听尺寸变化
63
+ const resizeObserver = new ResizeObserver(() => {
64
+ checkScrollBar();
65
+ });
66
+ // 观察容器和内容
67
+ resizeObserver.observe(containerEl);
68
+ if (containerEl.firstElementChild) {
69
+ resizeObserver.observe(containerEl.firstElementChild);
70
+ }
71
+ return () => {
72
+ resizeObserver.disconnect();
73
+ };
74
+ }, [checkScrollBar]);
75
+ // 滚动回调(带节流)
76
+ const scrollCallback = useCallback((rawEvent) => {
77
+ const now = Date.now();
78
+ // 节流控制
79
+ if (scrollThrottle > 0 && now - lastCallTime.current < scrollThrottle) {
80
+ // 清除之前的定时器
81
+ if (throttleTimer.current) {
82
+ clearTimeout(throttleTimer.current);
83
+ }
84
+ // 设置新的定时器,确保最后一次调用会被执行
85
+ throttleTimer.current = window.setTimeout(() => {
86
+ handleScroll(rawEvent);
87
+ }, scrollThrottle);
88
+ return;
89
+ }
90
+ lastCallTime.current = now;
91
+ handleScroll(rawEvent);
92
+ }, [scrollThrottle, reachTopThreshold, reachBottomThreshold]);
93
+ // 实际的滚动处理逻辑
94
+ const handleScroll = useCallback((rawEvent) => {
95
+ var _a, _b, _c, _d, _e, _f;
31
96
  const box = container.current;
97
+ if (!box)
98
+ return;
32
99
  // 已经滚动的距离
33
100
  const scrollTop = box.scrollTop;
34
101
  // 滚动容器的包含滚动内容的高度
35
102
  const contentHeight = box.scrollHeight;
36
103
  // 滚动容器的视口高度
37
104
  const containerHeight = Math.min(box.clientHeight, box.offsetHeight);
38
- // 加载指示的高度,如果加载指示不存在,则高度为0
39
- const loadingHeight = (_b = (_a = box.children.item(1)) === null || _a === void 0 ? void 0 : _a.offsetHeight) !== null && _b !== void 0 ? _b : 0;
105
+ // 最大可滚动距离
40
106
  const maxScroll = contentHeight - containerHeight;
107
+ // 计算滚动方向
108
+ const direction = scrollTop > lastScrollTop.current ? "downward" : "upward";
41
109
  // 生成滚动事件参数
42
110
  const event = {
43
111
  containerHeight,
44
112
  contentHeight,
45
113
  maxScroll,
46
114
  scrollTop,
47
- direction: scrollTop > top.current ? "downward" : "upward",
115
+ direction,
48
116
  rawEvent,
49
117
  };
50
- // 调用输入的滚动事件
51
- onScroll === null || onScroll === void 0 ? void 0 : onScroll(event);
52
- // 判断是否触发触顶事件
53
- if (event.direction === "upward" && scrollTop < reachTopThreshold) {
54
- onReachTop === null || onReachTop === void 0 ? void 0 : onReachTop(event);
118
+ // 调用通用滚动事件(使用 ref 中的最新回调)
119
+ (_b = (_a = callbacksRef.current).onScroll) === null || _b === void 0 ? void 0 : _b.call(_a, event);
120
+ // 触顶逻辑(防止重复触发)
121
+ if (direction === "upward" && scrollTop <= reachTopThreshold) {
122
+ if (!hasReachedTop.current) {
123
+ hasReachedTop.current = true;
124
+ hasReachedBottom.current = false; // 重置触底标记
125
+ (_d = (_c = callbacksRef.current).onReachTop) === null || _d === void 0 ? void 0 : _d.call(_c, event);
126
+ }
55
127
  }
56
- // 判断是否触发触底事件
57
- if (event.direction === "downward" && scrollTop > maxScroll - reachBottomThreshold - loadingHeight) {
58
- onReachBottom === null || onReachBottom === void 0 ? void 0 : onReachBottom(event);
128
+ else if (scrollTop > reachTopThreshold) {
129
+ hasReachedTop.current = false;
130
+ }
131
+ // 触底逻辑(防止重复触发)
132
+ if (direction === "downward" && scrollTop >= maxScroll - reachBottomThreshold) {
133
+ if (!hasReachedBottom.current) {
134
+ hasReachedBottom.current = true;
135
+ hasReachedTop.current = false; // 重置触顶标记
136
+ (_f = (_e = callbacksRef.current).onReachBottom) === null || _f === void 0 ? void 0 : _f.call(_e, event);
137
+ }
138
+ }
139
+ else if (scrollTop < maxScroll - reachBottomThreshold) {
140
+ hasReachedBottom.current = false;
59
141
  }
60
142
  // 更新scrollTop上次的值
61
- top.current = scrollTop;
62
- };
143
+ lastScrollTop.current = scrollTop;
144
+ }, [reachTopThreshold, reachBottomThreshold]);
145
+ // 清理节流定时器
146
+ useLayoutEffect(() => {
147
+ return () => {
148
+ if (throttleTimer.current) {
149
+ clearTimeout(throttleTimer.current);
150
+ }
151
+ };
152
+ }, []);
63
153
  // loading内容
64
154
  let showLoadingContent = null;
65
155
  if (showLoading) {
66
156
  if (!loadingContent) {
67
- showLoadingContent = (_jsxs(RowCenter, Object.assign({ css: [style.loading, loadingStyle] }, { children: [_jsx(Indicator, { barColor: "#333", barCount: 12 }), _jsx("p", { children: "\u6570\u636E\u52A0\u8F7D\u4E2D..." })] })));
157
+ showLoadingContent = (_jsxs(RowCenter, { css: [style.loading, loadingStyle], children: [_jsx(Indicator, { barColor: "#333", barCount: 12 }), _jsx("p", { children: "\u6570\u636E\u52A0\u8F7D\u4E2D..." })] }));
68
158
  }
69
159
  else {
70
160
  showLoadingContent = loadingContent;
71
161
  }
72
162
  }
73
- // container是否有滚动条
74
- const [hasScrollBar, setHasScrollBar] = useState(false);
75
- useEffect(() => {
76
- let hasScrollBar = container.current.scrollHeight > container.current.clientHeight;
77
- setHasScrollBar(hasScrollBar);
78
- });
79
- return (_jsxs("div", Object.assign({ css: [style.container, heightStyle, containerStyle], onScroll: scrollCallback, ref: container }, attrs, { children: [_jsx("div", Object.assign({ css: wrapperStyle, ref: child }, { children: children })), hasScrollBar && showLoadingContent] })));
163
+ return (_jsxs("div", Object.assign({ css: [style.container, heightStyle, containerStyle], onScroll: scrollCallback, ref: container }, attrs, { children: [_jsx("div", { css: wrapperStyle, children: children }), hasScrollBar && showLoadingContent] })));
80
164
  }
@@ -23,16 +23,16 @@ export function Toast(props) {
23
23
  setAnimation(animation);
24
24
  }, duration);
25
25
  return () => {
26
- window.clearInterval(timer);
26
+ window.clearTimeout(timer);
27
27
  };
28
- }, [position]);
28
+ }, [position, duration]);
29
29
  let showContent;
30
30
  const middleStyle = position === "middle" ? style.contentMiddle : undefined;
31
31
  if (React.isValidElement(content)) {
32
- showContent = _jsx("div", Object.assign({ css: [middleStyle, contentStyle] }, { children: content }));
32
+ showContent = _jsx("div", { css: [middleStyle, contentStyle], children: content });
33
33
  }
34
34
  else {
35
- showContent = (_jsx("p", Object.assign({ css: [style.content(radius), middleStyle, contentStyle] }, { children: content })));
35
+ showContent = (_jsx("p", { css: [style.content(radius), middleStyle, contentStyle], children: content }));
36
36
  }
37
37
  // toast消失动画结束触发
38
38
  const animationEnd = (event) => {
@@ -1,10 +1,40 @@
1
1
  import { Keyframes } from "@emotion/serialize";
2
- export declare const middleShowAnimation: Keyframes;
3
- export declare const middleHideAnimation: Keyframes;
4
- export declare const topShowAnimation: Keyframes;
5
- export declare const topHideAnimation: Keyframes;
6
- export declare const bottomShowAnimation: Keyframes;
7
- export declare const bottomHideAnimation: Keyframes;
2
+ export declare const middleShowAnimation: {
3
+ name: string;
4
+ styles: string;
5
+ anim: 1;
6
+ toString: () => string;
7
+ } & string;
8
+ export declare const middleHideAnimation: {
9
+ name: string;
10
+ styles: string;
11
+ anim: 1;
12
+ toString: () => string;
13
+ } & string;
14
+ export declare const topShowAnimation: {
15
+ name: string;
16
+ styles: string;
17
+ anim: 1;
18
+ toString: () => string;
19
+ } & string;
20
+ export declare const topHideAnimation: {
21
+ name: string;
22
+ styles: string;
23
+ anim: 1;
24
+ toString: () => string;
25
+ } & string;
26
+ export declare const bottomShowAnimation: {
27
+ name: string;
28
+ styles: string;
29
+ anim: 1;
30
+ toString: () => string;
31
+ } & string;
32
+ export declare const bottomHideAnimation: {
33
+ name: string;
34
+ styles: string;
35
+ anim: 1;
36
+ toString: () => string;
37
+ } & string;
8
38
  /**
9
39
  * 根据位置和类型获取动画
10
40
  * @param position
@@ -13,14 +43,14 @@ export declare const bottomHideAnimation: Keyframes;
13
43
  */
14
44
  export declare function getAnimation(position: "top" | "middle" | "bottom", type: "show" | "hide"): {
15
45
  keyframes: Keyframes;
16
- animation: import("@emotion/utils").SerializedStyles;
46
+ animation: import("@emotion/react").SerializedStyles;
17
47
  };
18
48
  export declare const style: {
19
- container(): import("@emotion/utils").SerializedStyles;
20
- top(offset: number): import("@emotion/utils").SerializedStyles;
21
- middle: import("@emotion/utils").SerializedStyles;
22
- bottom(offset: number): import("@emotion/utils").SerializedStyles;
23
- content: (radius?: number) => import("@emotion/utils").SerializedStyles;
49
+ container(): import("@emotion/react").SerializedStyles;
50
+ top(offset: number): import("@emotion/react").SerializedStyles;
51
+ middle: import("@emotion/react").SerializedStyles;
52
+ bottom(offset: number): import("@emotion/react").SerializedStyles;
53
+ content: (radius?: number) => import("@emotion/react").SerializedStyles;
24
54
  contentMiddle: {
25
55
  transform: string;
26
56
  };
@@ -1,64 +1,64 @@
1
1
  import { css, keyframes } from "@emotion/react";
2
2
  import { adaptive } from "../utils/cssUtil";
3
- export const middleShowAnimation = keyframes `
4
- from {
5
- opacity: 0;
6
- transform: translateX(-50%) scale(0.9);
7
- }
8
- to {
9
- opacity: 1;
10
- transform: translateX(-50%) scale(1);
11
- }
3
+ export const middleShowAnimation = keyframes `
4
+ from {
5
+ opacity: 0;
6
+ transform: translateX(-50%) scale(0.9);
7
+ }
8
+ to {
9
+ opacity: 1;
10
+ transform: translateX(-50%) scale(1);
11
+ }
12
12
  `;
13
- export const middleHideAnimation = keyframes `
14
- from {
15
- opacity: 1;
16
- transform: translateX(-50%) scale(1);
17
- }
18
- to {
19
- opacity: 0;
20
- transform: translateX(-50%) scale(0.9);
21
- }
13
+ export const middleHideAnimation = keyframes `
14
+ from {
15
+ opacity: 1;
16
+ transform: translateX(-50%) scale(1);
17
+ }
18
+ to {
19
+ opacity: 0;
20
+ transform: translateX(-50%) scale(0.9);
21
+ }
22
22
  `;
23
- export const topShowAnimation = keyframes `
24
- from {
25
- opacity: 0;
26
- transform: translate(-50%, -100%);
27
- }
28
- to {
29
- opacity: 1;
30
- transform: translate(-50%, 0);
31
- }
23
+ export const topShowAnimation = keyframes `
24
+ from {
25
+ opacity: 0;
26
+ transform: translate(-50%, -100%);
27
+ }
28
+ to {
29
+ opacity: 1;
30
+ transform: translate(-50%, 0);
31
+ }
32
32
  `;
33
- export const topHideAnimation = keyframes `
34
- from {
35
- opacity: 1;
36
- transform: translate(-50%, 0);
37
- }
38
- to {
39
- opacity: 0;
40
- transform: translate(-50%, -100%);
41
- }
33
+ export const topHideAnimation = keyframes `
34
+ from {
35
+ opacity: 1;
36
+ transform: translate(-50%, 0);
37
+ }
38
+ to {
39
+ opacity: 0;
40
+ transform: translate(-50%, -100%);
41
+ }
42
42
  `;
43
- export const bottomShowAnimation = keyframes `
44
- from {
45
- opacity: 0;
46
- transform: translate(-50%, 100%);
47
- }
48
- to {
49
- opacity: 1;
50
- transform: translate(-50%, 0);
51
- }
43
+ export const bottomShowAnimation = keyframes `
44
+ from {
45
+ opacity: 0;
46
+ transform: translate(-50%, 100%);
47
+ }
48
+ to {
49
+ opacity: 1;
50
+ transform: translate(-50%, 0);
51
+ }
52
52
  `;
53
- export const bottomHideAnimation = keyframes `
54
- from {
55
- opacity: 1;
56
- transform: translate(-50%, 0);
57
- }
58
- to {
59
- opacity: 0;
60
- transform: translate(-50%, 100%);
61
- }
53
+ export const bottomHideAnimation = keyframes `
54
+ from {
55
+ opacity: 1;
56
+ transform: translate(-50%, 0);
57
+ }
58
+ to {
59
+ opacity: 0;
60
+ transform: translate(-50%, 100%);
61
+ }
62
62
  `;
63
63
  /**
64
64
  * 根据位置和类型获取动画
package/build/index.d.ts CHANGED
@@ -14,6 +14,9 @@ export { createApp, history, getHistory } from './utils/createApp';
14
14
  export { createPortalDOM } from './utils/dom';
15
15
  export { useInterval } from './Effect/useInterval';
16
16
  export { useTick } from './Effect/useTick';
17
+ export { useUpdate } from './Effect/useUpdate';
18
+ export { useWindowResize } from './Effect/useWindowResize';
19
+ export { useViewport } from './Effect/useViewport';
17
20
  export { Ago } from './Ago';
18
21
  export { Container } from './Container';
19
22
  export { Flex, FlexItem } from './Flex';
package/build/index.js CHANGED
@@ -14,6 +14,9 @@ export { createApp, history, getHistory } from './utils/createApp';
14
14
  export { createPortalDOM } from './utils/dom';
15
15
  export { useInterval } from './Effect/useInterval';
16
16
  export { useTick } from './Effect/useTick';
17
+ export { useUpdate } from './Effect/useUpdate';
18
+ export { useWindowResize } from './Effect/useWindowResize';
19
+ export { useViewport } from './Effect/useViewport';
17
20
  export { Ago } from './Ago';
18
21
  export { Container } from './Container';
19
22
  export { Flex, FlexItem } from './Flex';
@@ -33,7 +33,9 @@ export declare class Countdown {
33
33
  stop(): void;
34
34
  /**
35
35
  * 格式化每次更新的值
36
- * @param remainTime
36
+ * 注意:format 的顺序决定了如何分配时间
37
+ * 例如:format='his' 时,72小时会显示为 72:00:00
38
+ * format='dhis' 时,72小时会显示为 3:0:00:00(3天)
37
39
  */
38
40
  formatValue(): CountdownValue;
39
41
  }
@@ -54,6 +54,7 @@ export class Countdown {
54
54
  (_d = this._onUpdate) === null || _d === void 0 ? void 0 : _d.call(this, this.formatValue());
55
55
  // 记录倒计时开启时的时间
56
56
  const start = Date.now();
57
+ // 使用 1000ms 间隔,避免每帧都执行(性能优化)
57
58
  this._stopTick = tick(() => {
58
59
  var _a, _b, _c, _d;
59
60
  // 获取倒计时已经持续的时间
@@ -72,7 +73,7 @@ export class Countdown {
72
73
  this.remain = currentRemain;
73
74
  (_d = this._onUpdate) === null || _d === void 0 ? void 0 : _d.call(this, this.formatValue());
74
75
  }
75
- });
76
+ }, 1000); // ← 添加 1000ms 间隔
76
77
  }
77
78
  // 停止倒计时
78
79
  stop() {
@@ -82,7 +83,9 @@ export class Countdown {
82
83
  }
83
84
  /**
84
85
  * 格式化每次更新的值
85
- * @param remainTime
86
+ * 注意:format 的顺序决定了如何分配时间
87
+ * 例如:format='his' 时,72小时会显示为 72:00:00
88
+ * format='dhis' 时,72小时会显示为 3:0:00:00(3天)
86
89
  */
87
90
  formatValue() {
88
91
  let remainTime = this.remain;
@@ -1,20 +1,21 @@
1
- import React from 'react';
2
- import { History } from 'history';
3
- import { ContainerProps } from '../Container';
4
- import { ContextValue } from '../context';
5
- export type RouteMethod = 'browser' | 'hash' | 'memory';
1
+ import React from "react";
2
+ import { History } from "history";
3
+ import { ContainerProps } from "../Container";
4
+ import { ContextValue } from "../context";
5
+ export type RouterMode = "browser" | "hash" | "memory";
6
6
  export type AwaitValue<T> = T | Promise<T>;
7
- export interface CreateAppOption extends Omit<ContainerProps, 'children'>, ContextValue {
8
- onBeforeRenderPage?: (pathname?: string) => AwaitValue<void>;
9
- onAfterRenderPage?: (pathname?: string) => AwaitValue<void>;
10
- onLoadingPage?: (pathname?: string) => AwaitValue<React.ReactNode>;
11
- renderPage?: (pathname?: string) => AwaitValue<React.ReactNode>;
12
- routeMethod?: RouteMethod;
13
- defaultRoute?: string;
7
+ export interface CreateAppOption extends Omit<ContainerProps, "children">, ContextValue {
8
+ onBefore?: (pathname: string) => AwaitValue<void>;
9
+ onAfter?: (pathname: string) => AwaitValue<void>;
10
+ loading?: (pathname: string) => AwaitValue<React.ReactNode>;
11
+ render?: (pathname: string) => AwaitValue<React.ReactNode>;
12
+ notFound?: (pathname: string) => AwaitValue<React.ReactNode>;
13
+ mode?: RouterMode;
14
+ default?: string;
14
15
  target: string | HTMLElement;
15
16
  }
16
17
  export declare let history: null | History;
17
- export declare function getHistory(routeMethod?: RouteMethod): History;
18
+ export declare function getHistory(mode?: RouterMode): History;
18
19
  /**
19
20
  * 创建带路由的APP对象,全局对象,绝大部分情况下只需要调用一次
20
21
  * @param option CreateAppOption
@@ -7,24 +7,24 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import { jsx as _jsx } from "@emotion/react/jsx-runtime";
11
- import { useCallback, useEffect, useState } from 'react';
12
- import { createRoot } from 'react-dom/client';
13
- import { createBrowserHistory, createHashHistory, createMemoryHistory, } from 'history';
14
- import { Container } from '../Container';
15
- import { setContextValue } from '../context';
16
- import pick from 'lodash/pick';
10
+ import { jsxs as _jsxs, jsx as _jsx } from "@emotion/react/jsx-runtime";
11
+ import { useCallback, useEffect, useState } from "react";
12
+ import { createRoot } from "react-dom/client";
13
+ import { createBrowserHistory, createHashHistory, createMemoryHistory, } from "history";
14
+ import { Container } from "../Container";
15
+ import { setContextValue } from "../context";
16
+ import pick from "lodash/pick";
17
17
  // 存储历史记录对象
18
18
  export let history = null;
19
19
  // 获取历史记录对象
20
- export function getHistory(routeMethod = 'browser') {
20
+ export function getHistory(mode = "browser") {
21
21
  if (history === null) {
22
22
  const createMap = {
23
23
  browser: createBrowserHistory,
24
24
  hash: createHashHistory,
25
25
  memory: createMemoryHistory,
26
26
  };
27
- history = createMap[routeMethod]();
27
+ history = createMap[mode]();
28
28
  }
29
29
  return history;
30
30
  }
@@ -35,25 +35,31 @@ export function getHistory(routeMethod = 'browser') {
35
35
  export function createApp(option) {
36
36
  return __awaiter(this, void 0, void 0, function* () {
37
37
  // 设置默认的路由方式
38
- if (!option.routeMethod ||
39
- ['browser', 'hash', 'memory'].indexOf(option.routeMethod) === -1) {
40
- option.routeMethod = 'browser';
38
+ if (!option.mode ||
39
+ ["browser", "hash", "memory"].indexOf(option.mode) === -1) {
40
+ option.mode = "browser";
41
41
  }
42
42
  // 设置默认路由路径
43
- if (!option.defaultRoute) {
44
- option.defaultRoute = '/index';
43
+ if (!option.default) {
44
+ option.default = "/index";
45
45
  }
46
46
  // 这里是为了确保历史记录对象在组件渲染之前一定存在
47
- history = getHistory(option.routeMethod);
47
+ history = getHistory(option.mode);
48
48
  // 提取关键数据
49
- const context = pick(option, ['minDocWidth', 'maxDocWidth']);
49
+ const context = pick(option, ["minDocWidth", "maxDocWidth"]);
50
50
  const containerProps = pick(option, [
51
- 'designWidth',
52
- 'globalStyle',
51
+ "designWidth",
52
+ "globalStyle",
53
53
  ]);
54
- const { onBeforeRenderPage, onAfterRenderPage, onLoadingPage, renderPage } = option;
54
+ const { onBefore, onAfter, loading, render, notFound } = option;
55
55
  // 设置上下文属性
56
56
  setContextValue(context);
57
+ // 规范化路径:移除首尾斜杠
58
+ const PATH_TRIM_REGEX = /^\/*|\/*$/g;
59
+ const normalizePath = (path) => {
60
+ const normalized = path.replace(PATH_TRIM_REGEX, "");
61
+ return normalized || option.default.replace(PATH_TRIM_REGEX, "");
62
+ };
57
63
  /**
58
64
  * 全局APP组件对象
59
65
  * @returns
@@ -61,50 +67,60 @@ export function createApp(option) {
61
67
  const App = () => {
62
68
  const [page, setPage] = useState(null);
63
69
  /**
64
- * 渲染一个新页面
70
+ * 加载并渲染页面
65
71
  */
66
- const showPage = useCallback((pathname) => __awaiter(this, void 0, void 0, function* () {
67
- const pathReg = /^\/*|\/*$/g;
68
- pathname = pathname.replace(pathReg, '');
69
- if (!pathname) {
70
- pathname = option.defaultRoute.replace(pathReg, '');
71
- }
72
- // 如果有loading,要先显示loading
73
- if (typeof onLoadingPage === 'function') {
74
- setPage(yield (onLoadingPage === null || onLoadingPage === void 0 ? void 0 : onLoadingPage(pathname)));
72
+ const loadAndRenderPage = useCallback((pathname) => __awaiter(this, void 0, void 0, function* () {
73
+ const normalizedPath = normalizePath(pathname);
74
+ // 如果有 loading 占位符,先显示
75
+ if (typeof loading === "function") {
76
+ setPage(yield loading(normalizedPath));
75
77
  }
76
- // 加载页面之前可能会存在的逻辑
77
- yield (onBeforeRenderPage === null || onBeforeRenderPage === void 0 ? void 0 : onBeforeRenderPage(pathname));
78
+ // 页面加载前钩子
79
+ yield (onBefore === null || onBefore === void 0 ? void 0 : onBefore(normalizedPath));
78
80
  // 加载并显示页面
79
- setPage(yield (renderPage === null || renderPage === void 0 ? void 0 : renderPage(pathname)));
80
- // 加载页面之后可能存在的逻辑
81
- yield (onAfterRenderPage === null || onAfterRenderPage === void 0 ? void 0 : onAfterRenderPage());
82
- }), []);
81
+ if (typeof render === "function") {
82
+ const pageContent = yield render(normalizedPath);
83
+ // 如果返回 null/undefined,视为页面未找到
84
+ if (pageContent === null || pageContent === undefined) {
85
+ if (typeof notFound === "function") {
86
+ setPage(yield notFound(normalizedPath));
87
+ }
88
+ else {
89
+ // 默认 404 页面
90
+ setPage(_jsxs("div", { children: ["Not Found: ", normalizedPath] }));
91
+ }
92
+ return;
93
+ }
94
+ setPage(pageContent);
95
+ }
96
+ // 页面加载后钩子
97
+ yield (onAfter === null || onAfter === void 0 ? void 0 : onAfter(normalizedPath));
98
+ }), [onBefore, onAfter, loading, render, notFound]);
83
99
  /**
84
- * 监听变化
100
+ * 监听路由变化
85
101
  */
86
102
  useEffect(() => {
87
103
  // 监听页面变化,一旦变化渲染新页面
88
104
  const unlisten = history.listen(({ location }) => {
89
- showPage(location.pathname);
105
+ loadAndRenderPage(location.pathname);
90
106
  });
91
- // 初始化时渲染一个页面
92
- showPage(history.location.pathname);
107
+ // 初始化时渲染当前路径对应的页面
108
+ loadAndRenderPage(history.location.pathname);
93
109
  // 卸载时,取消监听
94
110
  return unlisten;
95
- }, []);
111
+ }, [loadAndRenderPage]);
96
112
  return _jsx(Container, Object.assign({}, containerProps, { children: page }));
97
113
  };
98
114
  // 获取挂载对象
99
115
  let mount = null;
100
- if (typeof option.target === 'string') {
116
+ if (typeof option.target === "string") {
101
117
  mount = document.querySelector(option.target);
102
118
  }
103
119
  else if (option.target instanceof HTMLElement) {
104
120
  mount = option.target;
105
121
  }
106
122
  else {
107
- throw new Error('No mounted object is specified');
123
+ throw new Error("No mounted object is specified");
108
124
  }
109
125
  const root = createRoot(mount);
110
126
  root.render(_jsx(App, {}));