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.
Files changed (43) hide show
  1. package/README.md +766 -422
  2. package/build/Alert/style.js +41 -58
  3. package/build/AutoGrid/index.js +32 -43
  4. package/build/AutoGrid/style.d.ts +3 -4
  5. package/build/AutoGrid/style.js +7 -19
  6. package/build/CarouselNotice/style.js +6 -11
  7. package/build/Clickable/index.d.ts +1 -0
  8. package/build/Clickable/index.js +77 -41
  9. package/build/Container/index.js +46 -80
  10. package/build/Dialog/style.js +2 -2
  11. package/build/Fixed/index.d.ts +5 -0
  12. package/build/Fixed/index.js +37 -0
  13. package/build/Indicator/index.js +5 -5
  14. package/build/Loading/style.js +16 -17
  15. package/build/Overlay/index.js +4 -32
  16. package/build/ScrollView/index.js +65 -47
  17. package/build/ScrollView/style.js +12 -13
  18. package/build/Toast/index.js +5 -3
  19. package/build/Toast/style.js +11 -14
  20. package/build/context.d.ts +2 -3
  21. package/build/context.js +1 -4
  22. package/build/index.d.ts +1 -1
  23. package/build/index.js +1 -1
  24. package/build/utils/Countdown.js +11 -4
  25. package/build/utils/ago.js +39 -52
  26. package/build/utils/createApp.d.ts +1 -2
  27. package/build/utils/createApp.js +23 -12
  28. package/build/utils/cssUtil.d.ts +0 -9
  29. package/build/utils/cssUtil.js +0 -38
  30. package/build/utils/defaultScroll.d.ts +4 -4
  31. package/build/utils/defaultScroll.js +6 -15
  32. package/build/utils/dom.js +5 -8
  33. package/build/utils/jsonp.js +7 -4
  34. package/build/utils/request.js +23 -33
  35. package/build/utils/tick.js +7 -14
  36. package/build/utils/uniqKey.js +7 -7
  37. package/build/utils/wait.js +1 -1
  38. package/package.json +10 -10
  39. package/test/eslint.config.js +5 -2
  40. package/test/package.json +11 -11
  41. package/test/src/ago/index.jsx +3 -1
  42. package/test/src/dialog/index.module.css +1 -1
  43. package/test/src/index.jsx +3 -2
@@ -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
- const aYearAgo = now.subtract(1, 'year');
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: showNum,
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: `${showNum}年前`,
24
+ format: `${years}年${isFuture ? '后' : '前'}`,
26
25
  };
27
26
  }
28
- // 多少月前
29
- if (input.isBefore(aMonthAgo)) {
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: showNum,
30
+ num: months,
40
31
  unit: 'm',
41
- format: `${showNum}个月前`,
32
+ format: `${months}个月${isFuture ? '后' : '前'}`,
42
33
  };
43
34
  }
44
- // 多少天前
45
- if (input.isBefore(aDayAgo)) {
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: showNum,
38
+ num: days,
49
39
  unit: 'd',
50
- format: `${showNum}天前`,
40
+ format: `${days}天${isFuture ? '后' : '前'}`,
51
41
  };
52
42
  }
53
- // 多少小时前
54
- if (input.isBefore(aHourAgo)) {
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: showNum,
46
+ num: hours,
58
47
  unit: 'h',
59
- format: `${showNum}个小时前`,
48
+ format: `${hours}小时${isFuture ? '后' : '前'}`,
60
49
  };
61
50
  }
62
- // 多少分钟前
63
- if (input.isBefore(aMinuteAgo)) {
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: showNum,
54
+ num: minutes,
67
55
  unit: 'i',
68
- format: `${showNum}分钟前`,
56
+ format: `${minutes}分钟${isFuture ? '后' : '前'}`,
69
57
  };
70
58
  }
71
- // 多少秒前
72
- const showNum = now.unix() - input.unix();
73
- let format;
74
- if (showNum > 10) {
75
- format = `${showNum}秒前`;
76
- }
77
- else {
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: showNum,
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">, ContextValue {
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>;
@@ -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
- const pageContent = yield render(normalizedPath);
83
- // 如果返回 null/undefined,视为页面未找到
84
- if (pageContent === null || pageContent === undefined) {
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
- }), [onBefore, onAfter, loading, render, notFound]);
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
- else {
123
- throw new Error("No mounted object is specified");
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, {}));
@@ -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;
@@ -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, passiveSupported ? { capture: false, passive: false } : false);
16
+ document.documentElement.addEventListener('touchmove', touchMoveHandler, { capture: false, passive: false });
26
17
  },
27
18
  enable() {
28
- document.documentElement.removeEventListener('touchmove', touchMoveHandler, passiveSupported ? { capture: false } : false);
19
+ document.documentElement.removeEventListener('touchmove', touchMoveHandler, { capture: false });
29
20
  },
30
21
  };
@@ -20,15 +20,12 @@ export function createPortalDOM(point) {
20
20
  root.render(component);
21
21
  },
22
22
  unmount() {
23
- root.unmount();
24
- if (container instanceof HTMLDivElement) {
25
- if (typeof container.remove === 'function') {
26
- container.remove();
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
  }
@@ -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
  });
@@ -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
- return Promise.race([
214
- // 网络请求
215
- fetch(url, Object.assign(Object.assign({}, fetchOption), { signal: controller.signal }))
216
- .then((response) => {
217
- return response.json();
218
- })
219
- .then((result) => {
220
- return result;
221
- })
222
- .catch((error) => {
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: -10000,
233
- message: "An exception occurred in the network request",
225
+ code: -10001,
226
+ message: "Network request timeout",
234
227
  };
235
228
  return result;
236
- }),
237
- // 超时逻辑
238
- new Promise((resolve) => {
239
- window.setTimeout(() => {
240
- controller.abort(); // 取消请求
241
- const result = {
242
- code: -10001,
243
- message: "Network request timeout",
244
- };
245
- resolve(result);
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
  /**
@@ -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
- // 设置了tick的间隔
12
- if (interval && typeof interval === 'number') {
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
- // 本次tick的时间为上次的时间加上频率间隔
23
- lastTick = lastTick + interval;
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);
@@ -1,16 +1,16 @@
1
1
  let keyIndex = 0;
2
- let last = Date.now();
2
+ let lastTimestamp = 0;
3
3
  /**
4
4
  * 生成一个全局唯一的key
5
5
  * @returns
6
6
  */
7
7
  export function uniqKey() {
8
- keyIndex += 1;
9
- let now = Date.now();
10
- if (now !== last && keyIndex > 1e9) {
8
+ const now = Date.now();
9
+ // 时间戳变化时重置计数器
10
+ if (now !== lastTimestamp) {
11
11
  keyIndex = 0;
12
+ lastTimestamp = now;
12
13
  }
13
- const key = now.toString(36) + keyIndex.toString(36);
14
- last = now;
15
- return key;
14
+ keyIndex += 1;
15
+ return now.toString(36) + keyIndex.toString(36);
16
16
  }
@@ -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.4",
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.18",
38
+ "dayjs": "^1.11.19",
39
39
  "history": "^5.3.0",
40
- "lodash": "^4.17.21"
40
+ "lodash": "^4.17.23"
41
41
  },
42
42
  "peerDependencies": {
43
- "react": "^19.2.0",
44
- "react-dom": "^19.2.0"
43
+ "react": "^19.2.4",
44
+ "react-dom": "^19.2.4"
45
45
  },
46
46
  "devDependencies": {
47
- "@types/lodash": "^4.17.20",
48
- "@types/react": "^19.2.2",
49
- "@types/react-dom": "^19.2.2",
50
- "csstype": "^3.1.3",
51
- "rimraf": "^6.0.1",
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
  }
@@ -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: globals.browser,
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.1.1",
14
- "react-dom": "^19.1.1"
13
+ "react": "^19.2.4",
14
+ "react-dom": "^19.2.4"
15
15
  },
16
16
  "devDependencies": {
17
- "@eslint/js": "^9.36.0",
18
- "@types/react": "^19.1.16",
19
- "@types/react-dom": "^19.1.9",
20
- "@vitejs/plugin-react": "^5.0.4",
21
- "eslint": "^9.36.0",
22
- "eslint-plugin-react-hooks": "^5.2.0",
23
- "eslint-plugin-react-refresh": "^0.4.22",
24
- "globals": "^16.4.0",
25
- "vite": "^7.1.7"
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
  }
@@ -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={Date.now()} style={{ color: "red" }} />
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" }} />
@@ -1,5 +1,5 @@
1
1
  .contentBox {
2
- width: 7.5rem * 0.8;
2
+ width: calc(7.5rem * 0.8);
3
3
  height: 4rem;
4
4
  background: goldenrod;
5
5
  }