clxx 2.1.4 → 2.1.6
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.
- package/README.md +766 -422
- package/build/Alert/style.js +41 -58
- package/build/AutoGrid/index.js +32 -43
- package/build/AutoGrid/style.d.ts +3 -4
- package/build/AutoGrid/style.js +7 -19
- package/build/CarouselNotice/style.js +6 -11
- package/build/Clickable/index.d.ts +1 -0
- package/build/Clickable/index.js +77 -41
- package/build/Container/index.js +46 -80
- package/build/Dialog/style.js +2 -2
- package/build/Fixed/index.d.ts +5 -0
- package/build/Fixed/index.js +37 -0
- package/build/Indicator/index.js +5 -5
- package/build/Loading/style.js +16 -17
- package/build/Overlay/index.js +4 -32
- package/build/ScrollView/index.js +65 -47
- package/build/ScrollView/style.js +12 -13
- package/build/Toast/index.js +5 -3
- package/build/Toast/style.js +11 -14
- package/build/context.d.ts +2 -3
- package/build/context.js +1 -4
- package/build/index.d.ts +1 -1
- package/build/index.js +1 -1
- package/build/utils/Countdown.js +11 -4
- package/build/utils/ago.js +39 -52
- package/build/utils/createApp.d.ts +1 -2
- package/build/utils/createApp.js +23 -12
- package/build/utils/cssUtil.d.ts +0 -9
- package/build/utils/cssUtil.js +0 -38
- package/build/utils/defaultScroll.d.ts +4 -4
- package/build/utils/defaultScroll.js +6 -15
- package/build/utils/dom.js +5 -8
- package/build/utils/jsonp.js +7 -4
- package/build/utils/request.js +23 -33
- package/build/utils/tick.js +7 -14
- package/build/utils/uniqKey.js +7 -7
- package/build/utils/wait.js +1 -1
- package/package.json +10 -10
- package/test/eslint.config.js +5 -2
- package/test/package.json +11 -11
- package/test/src/ago/index.jsx +3 -1
- package/test/src/dialog/index.module.css +1 -1
- package/test/src/index.jsx +3 -2
package/build/utils/ago.js
CHANGED
|
@@ -6,80 +6,67 @@ import dayjs from 'dayjs';
|
|
|
6
6
|
export function ago(date) {
|
|
7
7
|
const now = dayjs();
|
|
8
8
|
const input = dayjs(date);
|
|
9
|
-
|
|
10
|
-
const aMonthAgo = now.subtract(1, 'month');
|
|
11
|
-
const aDayAgo = now.subtract(1, 'day');
|
|
12
|
-
const aHourAgo = now.subtract(1, 'hour');
|
|
13
|
-
const aMinuteAgo = now.subtract(1, 'minute');
|
|
14
|
-
// 多少年前
|
|
15
|
-
if (input.isBefore(aYearAgo)) {
|
|
16
|
-
const diff = now.year() - input.year();
|
|
17
|
-
const nYearsAgo = now.subtract(diff, 'year');
|
|
18
|
-
let showNum = diff;
|
|
19
|
-
if (input.isAfter(nYearsAgo)) {
|
|
20
|
-
showNum = diff - 1;
|
|
21
|
-
}
|
|
9
|
+
if (!input.isValid()) {
|
|
22
10
|
return {
|
|
23
|
-
num:
|
|
11
|
+
num: 0,
|
|
12
|
+
unit: 's',
|
|
13
|
+
format: '刚刚',
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
const isFuture = input.isAfter(now);
|
|
17
|
+
const from = isFuture ? now : input;
|
|
18
|
+
const to = isFuture ? input : now;
|
|
19
|
+
const years = to.diff(from, 'year');
|
|
20
|
+
if (years >= 1) {
|
|
21
|
+
return {
|
|
22
|
+
num: years,
|
|
24
23
|
unit: 'y',
|
|
25
|
-
format: `${
|
|
24
|
+
format: `${years}年${isFuture ? '后' : '前'}`,
|
|
26
25
|
};
|
|
27
26
|
}
|
|
28
|
-
|
|
29
|
-
if (
|
|
30
|
-
let showNum = 1;
|
|
31
|
-
for (let n = 2; n <= 12; n++) {
|
|
32
|
-
const nMonthAgo = now.subtract(n, 'month');
|
|
33
|
-
if (input.isAfter(nMonthAgo)) {
|
|
34
|
-
showNum = n - 1;
|
|
35
|
-
break;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
27
|
+
const months = to.diff(from, 'month');
|
|
28
|
+
if (months >= 1) {
|
|
38
29
|
return {
|
|
39
|
-
num:
|
|
30
|
+
num: months,
|
|
40
31
|
unit: 'm',
|
|
41
|
-
format: `${
|
|
32
|
+
format: `${months}个月${isFuture ? '后' : '前'}`,
|
|
42
33
|
};
|
|
43
34
|
}
|
|
44
|
-
|
|
45
|
-
if (
|
|
46
|
-
const showNum = Math.floor((now.unix() - input.unix()) / 86400);
|
|
35
|
+
const days = to.diff(from, 'day');
|
|
36
|
+
if (days >= 1) {
|
|
47
37
|
return {
|
|
48
|
-
num:
|
|
38
|
+
num: days,
|
|
49
39
|
unit: 'd',
|
|
50
|
-
format: `${
|
|
40
|
+
format: `${days}天${isFuture ? '后' : '前'}`,
|
|
51
41
|
};
|
|
52
42
|
}
|
|
53
|
-
|
|
54
|
-
if (
|
|
55
|
-
const showNum = Math.floor((now.unix() - input.unix()) / 3600);
|
|
43
|
+
const hours = to.diff(from, 'hour');
|
|
44
|
+
if (hours >= 1) {
|
|
56
45
|
return {
|
|
57
|
-
num:
|
|
46
|
+
num: hours,
|
|
58
47
|
unit: 'h',
|
|
59
|
-
format: `${
|
|
48
|
+
format: `${hours}小时${isFuture ? '后' : '前'}`,
|
|
60
49
|
};
|
|
61
50
|
}
|
|
62
|
-
|
|
63
|
-
if (
|
|
64
|
-
const showNum = Math.floor((now.unix() - input.unix()) / 60);
|
|
51
|
+
const minutes = to.diff(from, 'minute');
|
|
52
|
+
if (minutes >= 1) {
|
|
65
53
|
return {
|
|
66
|
-
num:
|
|
54
|
+
num: minutes,
|
|
67
55
|
unit: 'i',
|
|
68
|
-
format: `${
|
|
56
|
+
format: `${minutes}分钟${isFuture ? '后' : '前'}`,
|
|
69
57
|
};
|
|
70
58
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
format = '刚刚';
|
|
59
|
+
const seconds = to.diff(from, 'second');
|
|
60
|
+
if (seconds < 10) {
|
|
61
|
+
return {
|
|
62
|
+
num: seconds,
|
|
63
|
+
unit: 's',
|
|
64
|
+
format: isFuture ? '马上' : '刚刚',
|
|
65
|
+
};
|
|
79
66
|
}
|
|
80
67
|
return {
|
|
81
|
-
num:
|
|
68
|
+
num: seconds,
|
|
82
69
|
unit: 's',
|
|
83
|
-
format
|
|
70
|
+
format: `${seconds}秒${isFuture ? '后' : '前'}`,
|
|
84
71
|
};
|
|
85
72
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { History } from "history";
|
|
3
3
|
import { ContainerProps } from "../Container";
|
|
4
|
-
import { ContextValue } from "../context";
|
|
5
4
|
export type RouterMode = "browser" | "hash" | "memory";
|
|
6
5
|
export type AwaitValue<T> = T | Promise<T>;
|
|
7
|
-
export interface CreateAppOption extends Omit<ContainerProps, "children"
|
|
6
|
+
export interface CreateAppOption extends Omit<ContainerProps, "children"> {
|
|
8
7
|
onBefore?: (pathname: string) => AwaitValue<void>;
|
|
9
8
|
onAfter?: (pathname: string) => AwaitValue<void>;
|
|
10
9
|
loading?: (pathname: string) => AwaitValue<React.ReactNode>;
|
package/build/utils/createApp.js
CHANGED
|
@@ -12,7 +12,6 @@ import { useCallback, useEffect, useState } from "react";
|
|
|
12
12
|
import { createRoot } from "react-dom/client";
|
|
13
13
|
import { createBrowserHistory, createHashHistory, createMemoryHistory, } from "history";
|
|
14
14
|
import { Container } from "../Container";
|
|
15
|
-
import { setContextValue } from "../context";
|
|
16
15
|
import pick from "lodash/pick";
|
|
17
16
|
// 存储历史记录对象
|
|
18
17
|
export let history = null;
|
|
@@ -46,14 +45,11 @@ export function createApp(option) {
|
|
|
46
45
|
// 这里是为了确保历史记录对象在组件渲染之前一定存在
|
|
47
46
|
history = getHistory(option.mode);
|
|
48
47
|
// 提取关键数据
|
|
49
|
-
const context = pick(option, ["minDocWidth", "maxDocWidth"]);
|
|
50
48
|
const containerProps = pick(option, [
|
|
51
49
|
"designWidth",
|
|
52
50
|
"globalStyle",
|
|
53
51
|
]);
|
|
54
52
|
const { onBefore, onAfter, loading, render, notFound } = option;
|
|
55
|
-
// 设置上下文属性
|
|
56
|
-
setContextValue(context);
|
|
57
53
|
// 规范化路径:移除首尾斜杠
|
|
58
54
|
const PATH_TRIM_REGEX = /^\/*|\/*$/g;
|
|
59
55
|
const normalizePath = (path) => {
|
|
@@ -79,23 +75,38 @@ export function createApp(option) {
|
|
|
79
75
|
yield (onBefore === null || onBefore === void 0 ? void 0 : onBefore(normalizedPath));
|
|
80
76
|
// 加载并显示页面
|
|
81
77
|
if (typeof render === "function") {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
78
|
+
try {
|
|
79
|
+
const pageContent = yield render(normalizedPath);
|
|
80
|
+
// 如果返回 null/undefined,视为页面未找到
|
|
81
|
+
if (pageContent === null || pageContent === undefined) {
|
|
82
|
+
if (typeof notFound === "function") {
|
|
83
|
+
setPage(yield notFound(normalizedPath));
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
// 默认 404 页面
|
|
87
|
+
setPage(_jsxs("div", { children: ["Not Found: ", normalizedPath] }));
|
|
88
|
+
}
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
setPage(pageContent);
|
|
92
|
+
}
|
|
93
|
+
catch (_a) {
|
|
94
|
+
// 动态 import 失败等场景
|
|
85
95
|
if (typeof notFound === "function") {
|
|
86
96
|
setPage(yield notFound(normalizedPath));
|
|
87
97
|
}
|
|
88
98
|
else {
|
|
89
|
-
// 默认 404 页面
|
|
90
99
|
setPage(_jsxs("div", { children: ["Not Found: ", normalizedPath] }));
|
|
91
100
|
}
|
|
92
101
|
return;
|
|
93
102
|
}
|
|
94
|
-
setPage(pageContent);
|
|
95
103
|
}
|
|
96
104
|
// 页面加载后钩子
|
|
97
105
|
yield (onAfter === null || onAfter === void 0 ? void 0 : onAfter(normalizedPath));
|
|
98
|
-
}),
|
|
106
|
+
}),
|
|
107
|
+
// 所有外部变量在闭包创建时已捕获,不会变化
|
|
108
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
109
|
+
[]);
|
|
99
110
|
/**
|
|
100
111
|
* 监听路由变化
|
|
101
112
|
*/
|
|
@@ -119,8 +130,8 @@ export function createApp(option) {
|
|
|
119
130
|
else if (option.target instanceof HTMLElement) {
|
|
120
131
|
mount = option.target;
|
|
121
132
|
}
|
|
122
|
-
|
|
123
|
-
throw new Error("
|
|
133
|
+
if (!mount) {
|
|
134
|
+
throw new Error(`Mount target not found: ${typeof option.target === "string" ? option.target : "invalid element"}`);
|
|
124
135
|
}
|
|
125
136
|
const root = createRoot(mount);
|
|
126
137
|
root.render(_jsx(App, {}));
|
package/build/utils/cssUtil.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Interpolation, Theme } from '@emotion/react';
|
|
2
1
|
/**
|
|
3
2
|
* 匹配所有的CSS数值类型的值
|
|
4
3
|
*/
|
|
@@ -32,11 +31,3 @@ export interface SplitedValue {
|
|
|
32
31
|
* @param defaultUnit
|
|
33
32
|
*/
|
|
34
33
|
export declare function splitValue(value: number | string, defaultUnit?: string): SplitedValue;
|
|
35
|
-
/**
|
|
36
|
-
* 生成自适应的样式,仅供库内部使用
|
|
37
|
-
* 所有内部组件的默认设计尺寸约定为750
|
|
38
|
-
*
|
|
39
|
-
* @param style
|
|
40
|
-
* @returns
|
|
41
|
-
*/
|
|
42
|
-
export declare function adaptive(style: Record<string, Interpolation<Theme>>): import("@emotion/react").SerializedStyles;
|
package/build/utils/cssUtil.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { css } from '@emotion/react';
|
|
2
|
-
import { getContextValue } from '../context';
|
|
3
1
|
/**
|
|
4
2
|
* 匹配所有的CSS数值类型的值
|
|
5
3
|
*/
|
|
@@ -51,39 +49,3 @@ export function splitValue(value, defaultUnit = 'px') {
|
|
|
51
49
|
}
|
|
52
50
|
throw new Error('Invalid numeric format');
|
|
53
51
|
}
|
|
54
|
-
/**
|
|
55
|
-
* 生成自适应的样式,仅供库内部使用
|
|
56
|
-
* 所有内部组件的默认设计尺寸约定为750
|
|
57
|
-
*
|
|
58
|
-
* @param style
|
|
59
|
-
* @returns
|
|
60
|
-
*/
|
|
61
|
-
export function adaptive(style) {
|
|
62
|
-
const ctx = getContextValue();
|
|
63
|
-
const max = {};
|
|
64
|
-
const min = {};
|
|
65
|
-
const normal = {};
|
|
66
|
-
for (let name in style) {
|
|
67
|
-
let value = style[name];
|
|
68
|
-
if (typeof value !== 'number') {
|
|
69
|
-
normal[name] = value;
|
|
70
|
-
}
|
|
71
|
-
else if ([
|
|
72
|
-
'flex',
|
|
73
|
-
'flexGrow',
|
|
74
|
-
'flexShrink',
|
|
75
|
-
'lineHeight',
|
|
76
|
-
'fontWeight',
|
|
77
|
-
'zIndex',
|
|
78
|
-
].includes(name) &&
|
|
79
|
-
typeof value === 'number') {
|
|
80
|
-
normal[name] = value;
|
|
81
|
-
}
|
|
82
|
-
else {
|
|
83
|
-
normal[name] = (value * 100) / 750 + 'vw';
|
|
84
|
-
max[name] = (value * ctx.maxDocWidth) / 750 + 'px';
|
|
85
|
-
min[name] = (value * ctx.minDocWidth) / 750 + 'px';
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
return css(Object.assign(Object.assign({}, normal), { [`@media (min-width: ${ctx.maxDocWidth}px)`]: max, [`@media (max-width: ${ctx.minDocWidth}px)`]: min }));
|
|
89
|
-
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 检测是否支持passive事件绑定
|
|
3
|
-
*/
|
|
4
|
-
export declare let passiveSupported: boolean;
|
|
5
1
|
/**
|
|
6
2
|
* 禁用和启用默认滚动
|
|
3
|
+
*
|
|
4
|
+
* 注意:现代浏览器将 document/documentElement 上的 touchmove 监听器
|
|
5
|
+
* 默认视为 passive: true,此时 preventDefault() 会静默失效。
|
|
6
|
+
* 因此必须显式声明 passive: false 才能真正阻止默认滚动行为。
|
|
7
7
|
*/
|
|
8
8
|
export declare const defaultScroll: {
|
|
9
9
|
disable(): void;
|
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 检测是否支持passive事件绑定
|
|
3
|
-
*/
|
|
4
|
-
export let passiveSupported = false;
|
|
5
|
-
try {
|
|
6
|
-
window.addEventListener('test', () => undefined, Object.defineProperty({}, 'passive', {
|
|
7
|
-
get: function () {
|
|
8
|
-
passiveSupported = true;
|
|
9
|
-
},
|
|
10
|
-
}));
|
|
11
|
-
// eslint-disable-next-line no-empty
|
|
12
|
-
}
|
|
13
|
-
catch (err) { }
|
|
14
1
|
/**
|
|
15
2
|
* 触摸移动事件处理器
|
|
16
3
|
*/
|
|
@@ -19,12 +6,16 @@ const touchMoveHandler = (event) => {
|
|
|
19
6
|
};
|
|
20
7
|
/**
|
|
21
8
|
* 禁用和启用默认滚动
|
|
9
|
+
*
|
|
10
|
+
* 注意:现代浏览器将 document/documentElement 上的 touchmove 监听器
|
|
11
|
+
* 默认视为 passive: true,此时 preventDefault() 会静默失效。
|
|
12
|
+
* 因此必须显式声明 passive: false 才能真正阻止默认滚动行为。
|
|
22
13
|
*/
|
|
23
14
|
export const defaultScroll = {
|
|
24
15
|
disable() {
|
|
25
|
-
document.documentElement.addEventListener('touchmove', touchMoveHandler,
|
|
16
|
+
document.documentElement.addEventListener('touchmove', touchMoveHandler, { capture: false, passive: false });
|
|
26
17
|
},
|
|
27
18
|
enable() {
|
|
28
|
-
document.documentElement.removeEventListener('touchmove', touchMoveHandler,
|
|
19
|
+
document.documentElement.removeEventListener('touchmove', touchMoveHandler, { capture: false });
|
|
29
20
|
},
|
|
30
21
|
};
|
package/build/utils/dom.js
CHANGED
|
@@ -20,15 +20,12 @@ export function createPortalDOM(point) {
|
|
|
20
20
|
root.render(component);
|
|
21
21
|
},
|
|
22
22
|
unmount() {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
mountPoint.removeChild(container);
|
|
30
|
-
}
|
|
23
|
+
// 先从 DOM 移除容器,再卸载 React 树
|
|
24
|
+
// 避免 React 18+ 在已卸载的根上发出警告
|
|
25
|
+
if (container.parentNode) {
|
|
26
|
+
container.parentNode.removeChild(container);
|
|
31
27
|
}
|
|
28
|
+
root.unmount();
|
|
32
29
|
},
|
|
33
30
|
};
|
|
34
31
|
}
|
package/build/utils/jsonp.js
CHANGED
|
@@ -25,20 +25,23 @@ export function jsonp(url_1) {
|
|
|
25
25
|
}
|
|
26
26
|
const urlObject = new URL(url);
|
|
27
27
|
urlObject.searchParams.set(callbackName, funcName);
|
|
28
|
+
// 清理辅助函数
|
|
29
|
+
const cleanup = () => {
|
|
30
|
+
delete window[funcName];
|
|
31
|
+
script.remove();
|
|
32
|
+
};
|
|
28
33
|
// 创建全局script
|
|
29
34
|
const script = document.createElement('script');
|
|
30
35
|
script.src = urlObject.href;
|
|
31
36
|
document.body.appendChild(script);
|
|
32
37
|
script.onerror = (error) => {
|
|
38
|
+
cleanup();
|
|
33
39
|
reject(error);
|
|
34
40
|
};
|
|
35
41
|
// 创建全局函数
|
|
36
42
|
window[funcName] = (result) => {
|
|
43
|
+
cleanup();
|
|
37
44
|
resolve(result);
|
|
38
|
-
// 删除全局函数
|
|
39
|
-
delete window[funcName];
|
|
40
|
-
// 删除临时脚本
|
|
41
|
-
script.remove();
|
|
42
45
|
};
|
|
43
46
|
});
|
|
44
47
|
});
|
package/build/utils/request.js
CHANGED
|
@@ -210,42 +210,32 @@ export function sendRequest(option) {
|
|
|
210
210
|
return __awaiter(this, void 0, void 0, function* () {
|
|
211
211
|
const { url, fetchOption, timeout } = parseRequestOption(option);
|
|
212
212
|
const controller = new AbortController();
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
})
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if (error.name === 'AbortError') {
|
|
225
|
-
const result = {
|
|
226
|
-
code: -10001,
|
|
227
|
-
message: "Network request timeout",
|
|
228
|
-
};
|
|
229
|
-
return result;
|
|
230
|
-
}
|
|
213
|
+
// 超时定时器,请求完成后清除以防泄漏
|
|
214
|
+
const timeoutId = window.setTimeout(() => {
|
|
215
|
+
controller.abort();
|
|
216
|
+
}, timeout !== null && timeout !== void 0 ? timeout : 30000);
|
|
217
|
+
try {
|
|
218
|
+
const response = yield fetch(url, Object.assign(Object.assign({}, fetchOption), { signal: controller.signal }));
|
|
219
|
+
const result = yield response.json();
|
|
220
|
+
return result;
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
if (error.name === 'AbortError') {
|
|
231
224
|
const result = {
|
|
232
|
-
code: -
|
|
233
|
-
message: "
|
|
225
|
+
code: -10001,
|
|
226
|
+
message: "Network request timeout",
|
|
234
227
|
};
|
|
235
228
|
return result;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}, timeout !== null && timeout !== void 0 ? timeout : 30000);
|
|
247
|
-
}),
|
|
248
|
-
]);
|
|
229
|
+
}
|
|
230
|
+
const result = {
|
|
231
|
+
code: -10000,
|
|
232
|
+
message: "An exception occurred in the network request",
|
|
233
|
+
};
|
|
234
|
+
return result;
|
|
235
|
+
}
|
|
236
|
+
finally {
|
|
237
|
+
clearTimeout(timeoutId);
|
|
238
|
+
}
|
|
249
239
|
});
|
|
250
240
|
}
|
|
251
241
|
/**
|
package/build/utils/tick.js
CHANGED
|
@@ -4,42 +4,35 @@
|
|
|
4
4
|
* @param interval
|
|
5
5
|
*/
|
|
6
6
|
export function tick(callback, interval) {
|
|
7
|
-
// 执行状态,是否正在执行
|
|
8
7
|
let isRunning;
|
|
9
8
|
let frame;
|
|
10
9
|
let frameId;
|
|
11
|
-
//
|
|
12
|
-
if (
|
|
10
|
+
// 有效的正整数间隔才走间隔分支
|
|
11
|
+
if (typeof interval === 'number' && interval > 0) {
|
|
13
12
|
let lastTick = Date.now();
|
|
14
13
|
frame = () => {
|
|
15
|
-
if (!isRunning)
|
|
14
|
+
if (!isRunning)
|
|
16
15
|
return;
|
|
17
|
-
}
|
|
18
16
|
frameId = requestAnimationFrame(frame);
|
|
19
17
|
const now = Date.now();
|
|
20
|
-
// 每次间隔频率逻辑上保持一致,即使帧频不一致
|
|
21
18
|
if (now - lastTick >= interval) {
|
|
22
|
-
//
|
|
23
|
-
lastTick =
|
|
19
|
+
// 直接对齐到当前时间,避免长时间后台切回后的追赶风暴
|
|
20
|
+
lastTick = now;
|
|
24
21
|
callback();
|
|
25
22
|
}
|
|
26
23
|
};
|
|
27
24
|
}
|
|
28
|
-
// 没有设置tick的间隔
|
|
29
25
|
else {
|
|
26
|
+
// 没有设置 interval 或 interval <= 0 时,每帧执行
|
|
30
27
|
frame = () => {
|
|
31
|
-
if (!isRunning)
|
|
28
|
+
if (!isRunning)
|
|
32
29
|
return;
|
|
33
|
-
}
|
|
34
30
|
frameId = requestAnimationFrame(frame);
|
|
35
|
-
// 没有设置interval时,每帧都执行
|
|
36
31
|
callback();
|
|
37
32
|
};
|
|
38
33
|
}
|
|
39
|
-
// 开始执行
|
|
40
34
|
isRunning = true;
|
|
41
35
|
frameId = requestAnimationFrame(frame);
|
|
42
|
-
// 返回一个可以立即停止的函数
|
|
43
36
|
return () => {
|
|
44
37
|
isRunning = false;
|
|
45
38
|
cancelAnimationFrame(frameId);
|
package/build/utils/uniqKey.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
let keyIndex = 0;
|
|
2
|
-
let
|
|
2
|
+
let lastTimestamp = 0;
|
|
3
3
|
/**
|
|
4
4
|
* 生成一个全局唯一的key
|
|
5
5
|
* @returns
|
|
6
6
|
*/
|
|
7
7
|
export function uniqKey() {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
if (now !==
|
|
8
|
+
const now = Date.now();
|
|
9
|
+
// 时间戳变化时重置计数器
|
|
10
|
+
if (now !== lastTimestamp) {
|
|
11
11
|
keyIndex = 0;
|
|
12
|
+
lastTimestamp = now;
|
|
12
13
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return key;
|
|
14
|
+
keyIndex += 1;
|
|
15
|
+
return now.toString(36) + keyIndex.toString(36);
|
|
16
16
|
}
|
package/build/utils/wait.js
CHANGED
|
@@ -31,13 +31,13 @@ export function waitUntil(condition, maxTime) {
|
|
|
31
31
|
return new Promise((resolve) => {
|
|
32
32
|
const stop = tick(() => {
|
|
33
33
|
const now = Date.now();
|
|
34
|
-
const result = condition();
|
|
35
34
|
// 超时返回false
|
|
36
35
|
if (now - start >= maxTime) {
|
|
37
36
|
stop();
|
|
38
37
|
resolve(false);
|
|
39
38
|
return;
|
|
40
39
|
}
|
|
40
|
+
const result = condition();
|
|
41
41
|
// 处理结果
|
|
42
42
|
const handle = (res) => {
|
|
43
43
|
if (res) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clxx",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.6",
|
|
4
4
|
"description": "Basic JS library for mobile devices",
|
|
5
5
|
"main": "./build/index.js",
|
|
6
6
|
"module": "./build/index.js",
|
|
@@ -35,20 +35,20 @@
|
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@emotion/react": "^11.14.0",
|
|
38
|
-
"dayjs": "^1.11.
|
|
38
|
+
"dayjs": "^1.11.19",
|
|
39
39
|
"history": "^5.3.0",
|
|
40
|
-
"lodash": "^4.17.
|
|
40
|
+
"lodash": "^4.17.23"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"react": "^19.2.
|
|
44
|
-
"react-dom": "^19.2.
|
|
43
|
+
"react": "^19.2.4",
|
|
44
|
+
"react-dom": "^19.2.4"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@types/lodash": "^4.17.
|
|
48
|
-
"@types/react": "^19.2.
|
|
49
|
-
"@types/react-dom": "^19.2.
|
|
50
|
-
"csstype": "^3.
|
|
51
|
-
"rimraf": "^6.
|
|
47
|
+
"@types/lodash": "^4.17.24",
|
|
48
|
+
"@types/react": "^19.2.14",
|
|
49
|
+
"@types/react-dom": "^19.2.3",
|
|
50
|
+
"csstype": "^3.2.3",
|
|
51
|
+
"rimraf": "^6.1.3",
|
|
52
52
|
"typescript": "^5.9.3"
|
|
53
53
|
}
|
|
54
54
|
}
|
package/test/eslint.config.js
CHANGED
|
@@ -10,12 +10,15 @@ export default defineConfig([
|
|
|
10
10
|
files: ['**/*.{js,jsx}'],
|
|
11
11
|
extends: [
|
|
12
12
|
js.configs.recommended,
|
|
13
|
-
reactHooks.configs['recommended-latest'],
|
|
13
|
+
reactHooks.configs.flat['recommended-latest'],
|
|
14
14
|
reactRefresh.configs.vite,
|
|
15
15
|
],
|
|
16
16
|
languageOptions: {
|
|
17
17
|
ecmaVersion: 2020,
|
|
18
|
-
globals:
|
|
18
|
+
globals: {
|
|
19
|
+
...globals.browser,
|
|
20
|
+
...globals.node,
|
|
21
|
+
},
|
|
19
22
|
parserOptions: {
|
|
20
23
|
ecmaVersion: 'latest',
|
|
21
24
|
ecmaFeatures: { jsx: true },
|
package/test/package.json
CHANGED
|
@@ -10,18 +10,18 @@
|
|
|
10
10
|
"preview": "vite preview"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"react": "^19.
|
|
14
|
-
"react-dom": "^19.
|
|
13
|
+
"react": "^19.2.4",
|
|
14
|
+
"react-dom": "^19.2.4"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"@eslint/js": "^9.
|
|
18
|
-
"@types/react": "^19.
|
|
19
|
-
"@types/react-dom": "^19.
|
|
20
|
-
"@vitejs/plugin-react": "^5.
|
|
21
|
-
"eslint": "^9.
|
|
22
|
-
"eslint-plugin-react-hooks": "^
|
|
23
|
-
"eslint-plugin-react-refresh": "^0.
|
|
24
|
-
"globals": "^
|
|
25
|
-
"vite": "^7.1
|
|
17
|
+
"@eslint/js": "^9.39.3",
|
|
18
|
+
"@types/react": "^19.2.14",
|
|
19
|
+
"@types/react-dom": "^19.2.3",
|
|
20
|
+
"@vitejs/plugin-react": "^5.1.4",
|
|
21
|
+
"eslint": "^9.39.3",
|
|
22
|
+
"eslint-plugin-react-hooks": "^7.0.1",
|
|
23
|
+
"eslint-plugin-react-refresh": "^0.5.2",
|
|
24
|
+
"globals": "^17.4.0",
|
|
25
|
+
"vite": "^7.3.1"
|
|
26
26
|
}
|
|
27
27
|
}
|
package/test/src/ago/index.jsx
CHANGED
|
@@ -2,6 +2,8 @@ import React from "react";
|
|
|
2
2
|
import { Ago } from "@";
|
|
3
3
|
|
|
4
4
|
export default function Index() {
|
|
5
|
+
const now = 1583314718595;
|
|
6
|
+
|
|
5
7
|
return (
|
|
6
8
|
<div>
|
|
7
9
|
<p>2019-11-2</p>
|
|
@@ -17,7 +19,7 @@ export default function Index() {
|
|
|
17
19
|
<Ago date="2018/07/06 12:04:36" style={{ color: "red" }} />
|
|
18
20
|
<hr />
|
|
19
21
|
<p>Date.now()</p>
|
|
20
|
-
<Ago date={
|
|
22
|
+
<Ago date={now} style={{ color: "red" }} />
|
|
21
23
|
<hr />
|
|
22
24
|
<p>new Date("2012-07-16 12:30:06")</p>
|
|
23
25
|
<Ago date={new Date("2012-07-16 12:30:06")} style={{ color: "red" }} />
|