donar 0.0.1

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 (106) hide show
  1. package/.cspell/custom-dictionary.txt +5 -0
  2. package/.github/actions/base-check/action.yaml +27 -0
  3. package/.github/actions/env-setup/action.yaml +74 -0
  4. package/.github/workflows/pr-check.yaml +33 -0
  5. package/.github/workflows/release.yaml +112 -0
  6. package/.gitignore +41 -0
  7. package/.node-version +1 -0
  8. package/.npmignore +7 -0
  9. package/.vscode/extensions.json +10 -0
  10. package/.vscode/settings.json +44 -0
  11. package/LICENSE +21 -0
  12. package/README.md +160 -0
  13. package/commitlint.config.ts +3 -0
  14. package/cspell.json +27 -0
  15. package/dist/components.esm.js +2 -0
  16. package/dist/css/components.C24nnsjt.css +1 -0
  17. package/dist/hooks.esm.js +185 -0
  18. package/dist/index.esm.js +4 -0
  19. package/dist/js/components.nFDoAkCq.js +198 -0
  20. package/dist/js/utils.CVb1iSAU.js +330 -0
  21. package/dist/types/components.d.ts +1 -0
  22. package/dist/types/hooks.d.ts +1 -0
  23. package/dist/types/index.d.ts +1 -0
  24. package/dist/types/src/components/async-custom-show/index.d.ts +22 -0
  25. package/dist/types/src/components/carousel/hooks.d.ts +74 -0
  26. package/dist/types/src/components/carousel/index.d.ts +88 -0
  27. package/dist/types/src/components/custom-show/index.d.ts +21 -0
  28. package/dist/types/src/components/error-boundary/index.d.ts +31 -0
  29. package/dist/types/src/components/index.d.ts +8 -0
  30. package/dist/types/src/hooks/async-guard.d.ts +9 -0
  31. package/dist/types/src/hooks/index.d.ts +5 -0
  32. package/dist/types/src/hooks/observer.d.ts +70 -0
  33. package/dist/types/src/hooks/state.d.ts +44 -0
  34. package/dist/types/src/hooks/storage.d.ts +25 -0
  35. package/dist/types/src/hooks/timer.d.ts +16 -0
  36. package/dist/types/src/index.d.ts +3 -0
  37. package/dist/types/src/test/emiter-test.d.ts +1 -0
  38. package/dist/types/src/test/index.d.ts +1 -0
  39. package/dist/types/src/test/retry-async-test.d.ts +1 -0
  40. package/dist/types/src/utils/class-singleton.d.ts +6 -0
  41. package/dist/types/src/utils/concurrency.d.ts +17 -0
  42. package/dist/types/src/utils/debounce.d.ts +8 -0
  43. package/dist/types/src/utils/deep-copy.d.ts +11 -0
  44. package/dist/types/src/utils/dev.d.ts +8 -0
  45. package/dist/types/src/utils/download.d.ts +15 -0
  46. package/dist/types/src/utils/event-emitter/helpers.d.ts +36 -0
  47. package/dist/types/src/utils/event-emitter/index.d.ts +65 -0
  48. package/dist/types/src/utils/fetch-xhr/helpers.d.ts +28 -0
  49. package/dist/types/src/utils/fetch-xhr/index.d.ts +25 -0
  50. package/dist/types/src/utils/hash.d.ts +8 -0
  51. package/dist/types/src/utils/index.d.ts +15 -0
  52. package/dist/types/src/utils/is-deep-plain-equal.d.ts +15 -0
  53. package/dist/types/src/utils/json-convert.d.ts +66 -0
  54. package/dist/types/src/utils/micro-queue-scheduler.d.ts +14 -0
  55. package/dist/types/src/utils/raf-interval.d.ts +16 -0
  56. package/dist/types/src/utils/record-typed-map.d.ts +27 -0
  57. package/dist/types/src/utils/retry-async.d.ts +9 -0
  58. package/dist/types/src/utils/thenable.d.ts +15 -0
  59. package/dist/types/utils.d.ts +1 -0
  60. package/dist/utils.esm.js +2 -0
  61. package/eslint.config.ts +48 -0
  62. package/lint-staged.config.ts +13 -0
  63. package/oxfmt.config.ts +14 -0
  64. package/package.json +90 -0
  65. package/pnpm-workspace.yaml +3 -0
  66. package/scripts/sync-node-version/index.js +31 -0
  67. package/simple-git-hooks.js +4 -0
  68. package/src/components/async-custom-show/index.tsx +37 -0
  69. package/src/components/carousel/hooks.ts +312 -0
  70. package/src/components/carousel/index.module.scss +163 -0
  71. package/src/components/carousel/index.tsx +215 -0
  72. package/src/components/custom-show/index.tsx +31 -0
  73. package/src/components/error-boundary/index.tsx +48 -0
  74. package/src/components/index.ts +11 -0
  75. package/src/hooks/async-guard.ts +53 -0
  76. package/src/hooks/index.ts +5 -0
  77. package/src/hooks/observer.ts +236 -0
  78. package/src/hooks/state.ts +140 -0
  79. package/src/hooks/storage.ts +103 -0
  80. package/src/hooks/timer.ts +42 -0
  81. package/src/index.ts +3 -0
  82. package/src/test/emiter-test.ts +19 -0
  83. package/src/test/index.ts +35 -0
  84. package/src/test/retry-async-test.ts +8 -0
  85. package/src/utils/class-singleton.ts +23 -0
  86. package/src/utils/concurrency.ts +49 -0
  87. package/src/utils/debounce.ts +20 -0
  88. package/src/utils/deep-copy.ts +132 -0
  89. package/src/utils/dev.ts +16 -0
  90. package/src/utils/download.ts +39 -0
  91. package/src/utils/event-emitter/helpers.ts +80 -0
  92. package/src/utils/event-emitter/index.ts +171 -0
  93. package/src/utils/fetch-xhr/helpers.ts +85 -0
  94. package/src/utils/fetch-xhr/index.ts +103 -0
  95. package/src/utils/hash.ts +25 -0
  96. package/src/utils/index.ts +18 -0
  97. package/src/utils/is-deep-plain-equal.ts +45 -0
  98. package/src/utils/json-convert.ts +257 -0
  99. package/src/utils/micro-queue-scheduler.ts +38 -0
  100. package/src/utils/raf-interval.ts +42 -0
  101. package/src/utils/record-typed-map.ts +38 -0
  102. package/src/utils/retry-async.ts +30 -0
  103. package/src/utils/thenable.ts +30 -0
  104. package/tsconfig.json +43 -0
  105. package/types/scss.d.ts +10 -0
  106. package/vite.config.ts +51 -0
@@ -0,0 +1,5 @@
1
+ Behaviour
2
+ oxfmt
3
+ sonion
4
+ Tonar
5
+ Uncapitalize
@@ -0,0 +1,27 @@
1
+ name: Common Checks
2
+ description: 运行 TypeScript 编译检查和 ESLint 检查
3
+
4
+ runs:
5
+ using: composite
6
+ steps:
7
+ # TypeScript 编译检查
8
+ - name: TypeScript 检查
9
+ shell: bash
10
+ run: |
11
+ echo "运行 tsc 检查..."
12
+ pnpm exec tsc --noEmit
13
+ echo "tsc 检查通过 ✅"
14
+
15
+ # Lint 检查
16
+ - name: ESLint 检查
17
+ shell: bash
18
+ run: |
19
+ echo "运行 ESLint 检查..."
20
+ pnpm run lint
21
+ echo "Lint 检查通过 ✅"
22
+ - name: Oxfmt 检查
23
+ shell: bash
24
+ run: |
25
+ echo "运行 Oxfmt 检查..."
26
+ pnpm run fmt:check
27
+ echo "Oxfmt 检查通过 ✅"
@@ -0,0 +1,74 @@
1
+ name: Common Environment Setup
2
+ description: 配置 Node.js 环境和 pnpm 安装项目依赖
3
+
4
+ runs:
5
+ using: composite
6
+ steps:
7
+ - name: Print Current Workflow Trigger Info # 打印 当前触发工作流的引用和提交 SHA
8
+ shell: bash
9
+ run: |
10
+ echo "当前触发工作流的引用: $GITHUB_REF"
11
+ echo "当前触发工作流的提交 SHA: $GITHUB_SHA"
12
+
13
+ # 检查并安装 jq(如果不存在)
14
+ - name: Check and install jq
15
+ shell: bash
16
+ run: |
17
+ if ! command -v jq &> /dev/null; then
18
+ echo "jq 未找到,开始安装..."
19
+ apt-get update && apt-get install -y jq
20
+ echo "jq 安装完成"
21
+ else
22
+ echo "jq 已存在,版本: $(jq --version)"
23
+ fi
24
+
25
+ # 获取 pnpm 版本(来自 package.json engines)
26
+ - name: 读取 pnpm 版本
27
+ id: read-pnpm-version
28
+ shell: bash
29
+ run: |
30
+ PNPM_VERSION=$(jq -r '.engines.pnpm' package.json | grep -oE '[0-9]+(\.[0-9]+)*' | head -n1)
31
+ echo "pnpm-version=$PNPM_VERSION" >> $GITHUB_OUTPUT
32
+
33
+ - name: 全局安装 pnpm
34
+ uses: pnpm/action-setup@v4
35
+ with:
36
+ run_install: false # 不运行 pnpm install
37
+ # version: ^10.28.2 # 不写version会读取package.json 的 packageManager
38
+ version: ${{ steps.read-pnpm-version.outputs.pnpm-version }}
39
+
40
+ - name: 安装 Node.js
41
+ uses: actions/setup-node@v4
42
+ with:
43
+ node-version-file: '.node-version'
44
+ # node-version: 24.13.0 # 建议用 LTS 版本,稳定性更好
45
+ registry-url: 'https://registry.npmjs.org/'
46
+ cache: 'pnpm'
47
+
48
+ # 调试 Node 环境(新增)
49
+ - name: 调试 Node.js 环境
50
+ shell: bash
51
+ run: |
52
+ echo "👉 Node.js 路径: $(which node)"
53
+ echo "👉 Node.js 版本: $(node -v)"
54
+ echo "👉 npm 路径: $(which npm)"
55
+ echo "👉 npm 版本: $(npm -v)"
56
+ echo "👉 NODE_PATH: $NODE_PATH"
57
+ echo "👉 PATH: $PATH"
58
+
59
+ # 调试 pnpm 环境
60
+ - name: 调试 pnpm 环境
61
+ shell: bash
62
+ run: |
63
+ echo "👉 pnpm 路径: $(which pnpm)"
64
+ echo "👉 pnpm 版本: $(pnpm -v)"
65
+ echo "👉 pnpm 全局路径: $(pnpm root -g)"
66
+ echo "👉 PATH: $PATH"
67
+
68
+ # 使用 pnpm 安装依赖 等价于 npm ci
69
+ - name: 安装依赖(使用 pnpm)
70
+ shell: bash
71
+ run: |
72
+ echo "开始安装依赖..."
73
+ pnpm install --frozen-lockfile --verbose
74
+ echo "依赖安装完成 ✅"
@@ -0,0 +1,33 @@
1
+ name: PR Check
2
+ description: 运行 PR 检查
3
+
4
+ on:
5
+ pull_request:
6
+ branches:
7
+ - main
8
+ - dev
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ jobs:
14
+ pr-checks:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ # 使用本地 composite action 前需要先 checkout 代码
18
+ # 否则,GitHub Actions 无法找到本地的 action.yml 文件
19
+ - name: Checkout
20
+ uses: actions/checkout@v4 # 拉取触发工作流的 tag 对应 commit
21
+ with:
22
+ fetch-depth: 0 # 拉取完整历史。当仓库历史多时可能变慢,可考虑用 GitHub API compare 优化
23
+ - name: 准备环境
24
+ uses: ./.github/actions/env-setup # 引入 环境准备
25
+ - name: 基础检查
26
+ uses: ./.github/actions/base-check # 引入 基础检查
27
+
28
+ # Commitlint 检查(检查 PR 中所有 commits)
29
+ - name: Commitlint 检查
30
+ run: |
31
+ echo "运行 Commitlint 检查..."
32
+ pnpm exec commitlint --from=${{ github.event.pull_request.base.sha }} --to=${{ github.event.pull_request.head.sha }}
33
+ echo "Commitlint 检查通过 ✅"
@@ -0,0 +1,112 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*.*.*" # 例如 v1.2.3
7
+
8
+ # Trusted Publisher 权限
9
+ permissions:
10
+ id-token: write # Required for OIDC
11
+ contents: write # Required for Release
12
+
13
+ jobs:
14
+ build-and-publish:
15
+ if: github.ref_type == 'tag' && startsWith(github.ref, 'refs/tags/v') && github.event.base_ref == 'refs/heads/main'
16
+ runs-on: ubuntu-latest # 使用最新的 Ubuntu Runner
17
+ # environment: production-publish-npm # 指定环境名
18
+
19
+ steps:
20
+ # 使用本地 composite action 前需要先 checkout 代码
21
+ # 否则,GitHub Actions 无法找到本地的 action.yml 文件
22
+ - name: Checkout
23
+ uses: actions/checkout@v4 # 拉取触发工作流的 tag 对应 commit
24
+ with:
25
+ fetch-depth: 0 # 拉取完整历史。当仓库历史多时可能变慢,可考虑用 GitHub API compare 优化
26
+
27
+ - name: 准备环境
28
+ uses: ./.github/actions/env-setup # 引入 环境准备
29
+ - name: 基础检查
30
+ uses: ./.github/actions/base-check # 引入 基础检查
31
+
32
+ # 找到上一个 tag
33
+ - name: 获取上一个 Tag
34
+ id: prev-tag
35
+ run: |
36
+ # 获取当前 tag
37
+ CURRENT_TAG=${GITHUB_REF#refs/tags/}
38
+ echo "当前 tag: $CURRENT_TAG"
39
+
40
+ # 找到上一个 tag(按时间排序)
41
+ # 如果当前 tag 是第一个 tag,则检查所有 commit
42
+ PREV_TAG=$(git describe --tags --abbrev=0 ${CURRENT_TAG}^ 2>/dev/null || echo "")
43
+
44
+ if [ -z "$PREV_TAG" ]; then
45
+ echo "当前是第一个 tag,将检查所有 commit"
46
+ echo "prev_tag=" >> $GITHUB_OUTPUT
47
+ else
48
+ echo "上一个 tag: $PREV_TAG"
49
+ echo "prev_tag=$PREV_TAG" >> $GITHUB_OUTPUT
50
+ fi
51
+
52
+ # Commitlint 检查(从上一个 tag 到当前 tag)
53
+ - name: Commit 提交信息检查
54
+ run: |
55
+ echo "运行 Commitlint 检查..."
56
+
57
+ # 如果 prev_tag 为空(第一个 tag),则检查所有 commit
58
+ if [ -z "${{ steps.prev-tag.outputs.prev_tag }}" ]; then
59
+ pnpm exec commitlint --from=$(git rev-list --max-parents=0 HEAD) --to=${GITHUB_SHA}
60
+ else
61
+ pnpm exec commitlint --from=$(git rev-list -n 1 ${{ steps.prev-tag.outputs.prev_tag }}) --to=${GITHUB_SHA}
62
+ fi
63
+ echo "Commitlint 检查通过 ✅"
64
+
65
+ # tag 同步 package.json 版本 依赖jq
66
+ - name: 同步 Tag 为 package.json 版本
67
+ run: |
68
+ set -e
69
+ TAG=${GITHUB_REF#refs/tags/}
70
+ VERSION=${TAG#v}
71
+ echo "当前 Tag: $TAG,提取版本号: $VERSION"
72
+ if [ -z "$VERSION" ]; then
73
+ echo "错误:版本号为空,请检查 Tag 格式(应为 vX.Y.Z)"
74
+ exit 1
75
+ fi
76
+ jq ".version = \"$VERSION\"" package.json > package.json.tmp
77
+ mv package.json.tmp package.json
78
+ echo "更新后 package.json 版本: $(cat package.json | grep version)"
79
+
80
+ - name: 编译
81
+ run: |
82
+ echo "开始构建..."
83
+ pnpm run build
84
+ echo "构建完成 ✅"
85
+
86
+ # 打印将要发布的文件列表,避免误发布
87
+ - name: 打印发布文件列表
88
+ run: npm pack --dry-run
89
+
90
+ # 发布到 npm registry - 只在标签触发时执行
91
+ - name: Publish 到 npm
92
+ run: |
93
+ echo "开始发布到 npm..."
94
+ npm publish --access public --verbose
95
+ echo "发布成功 ✅"
96
+ env:
97
+ NODE_AUTH_TOKEN: ${{ secrets.PUBLISH_TOKEN }} # 首次发布需要
98
+
99
+ # 只有发布成功后才生成 GitHub Release
100
+ - name: 创建 GitHub Release 记录
101
+ uses: actions/create-release@v1
102
+ env:
103
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
104
+ with:
105
+ tag_name: ${{ github.ref_name }}
106
+ release_name: Release ${{ github.ref_name }}
107
+ body: |
108
+ 🎉 自动生成的 Release
109
+ - 发布版本: ${{ github.ref_name }}
110
+ - 提交 SHA: ${{ github.sha }}
111
+ draft: false
112
+ prerelease: false
package/.gitignore ADDED
@@ -0,0 +1,41 @@
1
+ # Logs
2
+ logs
3
+ *.log
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+ pnpm-debug.log*
8
+ lerna-debug.log*
9
+
10
+ # Dependency directories
11
+ node_modules
12
+ dist
13
+ dist-ssr
14
+
15
+ # Editor directories and files
16
+ .vscode/*
17
+ !.vscode/settings.json
18
+ !.vscode/extensions.json
19
+ .idea
20
+ *.suo
21
+ *.ntvs*
22
+ *.njsproj
23
+ *.sln
24
+ *.sw?
25
+
26
+ # typescript
27
+ *.tsbuildinfo
28
+
29
+ # Git
30
+ .git
31
+
32
+ # OS
33
+ .DS_Store
34
+ # windows 缩略图缓存
35
+ .Thumbs.db
36
+
37
+ # Misc
38
+ *.local
39
+ *.tmp
40
+ *.cache
41
+
package/.node-version ADDED
@@ -0,0 +1 @@
1
+ 24.14.0
package/.npmignore ADDED
@@ -0,0 +1,7 @@
1
+ node_modules
2
+ # 发布 不忽略 dist 目录
3
+ !dist
4
+ # 不生效
5
+ !.gitignore
6
+ # 不生效
7
+ !.npmignore
@@ -0,0 +1,10 @@
1
+ {
2
+ // 推荐安装的插件
3
+ "recommendations": [
4
+ "dbaeumer.vscode-eslint",
5
+ "oxc.oxc-vscode",
6
+ "streetsidesoftware.code-spell-checker"
7
+ ]
8
+ // 不希望安装的插件
9
+ // "unwantedRecommendations": ["some.extension.you.dont.want"]
10
+ }
@@ -0,0 +1,44 @@
1
+ {
2
+ // 保存时自动格式化
3
+ "editor.formatOnSave": true,
4
+ // 格式化配置
5
+ "oxc.fmt.configPath": "oxfmt.config.ts",
6
+ // 必须用 per-language 指定,否则会被用户级设置中的 defaultFormatter 覆盖
7
+ // Cmd+Shift+P → Preferences: Open User Settings (JSON) 可查看用户级设置
8
+ // 工作区 per-language > 用户 per-language > 工作区 全局 > 用户 全局
9
+ "[javascript]": {
10
+ "editor.defaultFormatter": "oxc.oxc-vscode"
11
+ },
12
+ "[typescript]": {
13
+ "editor.defaultFormatter": "oxc.oxc-vscode"
14
+ },
15
+ "[javascriptreact]": {
16
+ "editor.defaultFormatter": "oxc.oxc-vscode"
17
+ },
18
+ "[typescriptreact]": {
19
+ "editor.defaultFormatter": "oxc.oxc-vscode"
20
+ },
21
+ "[html]": {
22
+ "editor.defaultFormatter": "oxc.oxc-vscode"
23
+ },
24
+ "[css]": {
25
+ "editor.defaultFormatter": "oxc.oxc-vscode"
26
+ },
27
+ "[scss]": {
28
+ "editor.defaultFormatter": "oxc.oxc-vscode"
29
+ },
30
+
31
+ // 保存时自动修复 eslint 错误
32
+ "editor.codeActionsOnSave": {
33
+ "source.fixAll.eslint": "explicit"
34
+ },
35
+ // 需要检查的文件类型
36
+ "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
37
+
38
+ // JSDoc 注释配置
39
+ "fileheader.configObj": {
40
+ "colon": [" ", " "],
41
+ "functionParamsShape": ["", ""],
42
+ "functionTypeSymbol": ""
43
+ }
44
+ }
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 sonion
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,160 @@
1
+ # Tonar
2
+
3
+ **Tonar** 是一个现代前端工具库,提供常用的 **工具函数 (Utils)**、**React 钩子函数 (Hooks)** 和 **React 组件 (Components)**。
4
+
5
+ - ✅ 支持 **React 18+**
6
+ - ✅ 支持 **TypeScript**
7
+ - ✅ 仅支持 **ESM(不支持 CommonJS)**
8
+ - ✅ 支持 **Tree-shaking**
9
+
10
+ Tonar is a modern frontend library that provides **utility functions**, **React hooks**, and **React components**.
11
+
12
+ - ✅ Supports **React 18+**
13
+ - ✅ Supports **TypeScript**
14
+ - ✅ Only supports **ESM (does not support CommonJS)**
15
+ - ✅ Supports **Tree-shaking**
16
+
17
+ ---
18
+
19
+ ## 📦 Installation / 安装
20
+
21
+ ```bash
22
+ npm install tonar@latest
23
+ pnpm add tonar@latest
24
+ yarn add tonar@latest
25
+ ```
26
+
27
+ ## 🚀 Usage / 使用方法
28
+
29
+ #### 📥 Unified Import(统一导入:utils、hooks、components)
30
+
31
+ ```ts
32
+ import {
33
+ Carousel,
34
+ useDistinctState,
35
+ rAfInterval,
36
+ type RAfIntervalReturn,
37
+ // ...other components、hooks、utils
38
+ } from 'tonar';
39
+ ```
40
+
41
+ #### 🧩 Components / 组件
42
+
43
+ - Carousel (轮播组件,支持轮播项宽度小于容器宽度)
44
+ - CustomShow (条件展示组件)
45
+ - AsyncCustomShow (异步条件展示组件)
46
+ - ErrorBoundary (错误边界组件)
47
+
48
+ ```js
49
+ import {
50
+ Carousel,
51
+ CustomShow,
52
+ // ...other components
53
+ } from 'tonar/components';
54
+ ```
55
+
56
+ #### 🔗 Hooks / 钩子函数
57
+
58
+ - useDistinctState (差异才更新的状态,支持onChange事件和自定义差异对比函数)
59
+ - useStaticState (静态属性,不触发react更新)
60
+ - useCreateSafeRef (安全引用,相同不更新,改变可触发更新, 支持自定义差异对比函数)
61
+ - useLatestCallback (保持稳定的最新回调,稳定引用函数与闭包获取新值不可兼得的问题)
62
+ - useAsyncActionLock (异步操作锁,根据传入异步函数确定是否可再触发,并提供运行中状态)
63
+ - useIntersectionObserver (交叉观察器)
64
+ - useMutationObserver (节点变化观察器)
65
+ - useResizeObserver (尺寸变化观察器)
66
+ - useInterval (定时器)
67
+ - useRAfInterval (RAf 定时器)
68
+ - useStorage (支持事件的本地存储)
69
+
70
+ ```js
71
+ import {
72
+ useCreateSafeRef,
73
+ useDistinctState,
74
+ // ...other hooks
75
+ } from 'tonar/hooks';
76
+ ```
77
+
78
+ #### 🛠️ Utils / 工具函数
79
+
80
+ - singleton (创建单例类,不可通过原型链绕过)
81
+ - ConcurrencyController (并发控制器)
82
+ - safeAwait (安全 await,通过返回状态处理 reject 情况,避免 try-catch 嵌套)
83
+ - fetchXHR (基于 XMLHttpRequest 的 fetch 实现,支持超时、取消请求、进度回调)
84
+ - EventEmitter (事件中心。支持自定义调度器、API对齐原生事件、类型安全,支持 “事件类型/事件参数” 泛型)
85
+ - createMicroQueueScheduler (创建微队列调度器,同一个同步执行阶段中的所有任务合并到一个微任务中执行)
86
+ - RecordTypedMap (Record类型化Map,键和值的类型一一对应。通过对象类型定义)
87
+ - retryAsync (重试任务,支持重试次数、动态间隔时间,可指数退避 或 线性递增)
88
+ - isDeepPlainEqual (深度比较两个未知类型的值是否相等。引用不同,值相等返回 true)
89
+ - convertSnake2Camel (对象属性名,蛇形命名转小驼峰命名)
90
+ - convertCamel2Snake (对象属性名,小驼峰命名转蛇形命名)
91
+ - convertPascal2Camel (对象属性名,大驼峰命名转小驼峰命名)
92
+ - convertCamel2Pascal (对象属性名,小驼峰命名转大驼峰命名)
93
+ - deepClone (深拷贝,支持Set、Map、ExpReg、Date、循环引用)
94
+ - debounce (防抖)
95
+ - stringToHash (字符串转哈希值)
96
+ - browserNativeDownload (浏览器原生下载,支持检测是否被浏览器拦截)
97
+ - blobDownload (Blob 下载)
98
+ - rAfInterval (RAf 定时器)
99
+ - clearRAfInterval (清除 RAf 定时器)
100
+
101
+ ```js
102
+ import {
103
+ debounce,
104
+ ConcurrencyController,
105
+ // ...other utils
106
+ } from 'tonar/utils';
107
+ ```
108
+
109
+ ## 📖 Example / 示例
110
+
111
+ #### 一个简单的 React 页面同时使用 Carousel 组件、Hook 和 Utils:
112
+
113
+ ```tsx
114
+ import React from 'react';
115
+ import { Carousel, ErrorBoundary } from 'tonar/components';
116
+ import { useDistinctState } from 'tonar/hooks';
117
+ import { debounce } from 'tonar/utils';
118
+
119
+ export default function App() {
120
+ // 支持onChange事件和自定义差异对比函数
121
+ const [count, setCount] = useDistinctState({ initialValue: 0 });
122
+
123
+ const handleClick = debounce(() => {
124
+ setCount(count + 1); // 仅值不相等时更新
125
+ }, 300);
126
+
127
+ return (
128
+ <ErrorBoundary fallback={<div>Something went wrong</div>}>
129
+ <h1>Tonar Demo</h1>
130
+ <button onClick={handleClick}>Click Me ({count})</button>
131
+ <Carousel cardWidth={200} cardHeight={120}>
132
+ <div style={{ background: 'lightblue' }}>Slide 1</div>
133
+ <div style={{ background: 'lightgreen' }}>Slide 2</div>
134
+ <div style={{ background: 'lightpink' }}>Slide 3</div>
135
+ </Carousel>
136
+ </ErrorBoundary>
137
+ );
138
+ }
139
+ ```
140
+
141
+ ## 📚 TypeScript tips / 类型导入提示
142
+
143
+ #### 从包根目录直接导入类型(例如组件 Props):
144
+
145
+ ```ts
146
+ import { type CarouselProps } from 'tonar';
147
+ ```
148
+
149
+ #### 或按子路径导入(如果你更喜欢明确的来源):
150
+
151
+ ```ts
152
+ import type { CarouselProps } from 'tonar/components';
153
+ ```
154
+
155
+ ## 📝 License / 许可证
156
+
157
+ MIT © Sonion
158
+
159
+ 欢迎 [Pull Requests](https://github.com/sonion028/tonar/pulls) 和 [Issues](https://github.com/sonion028/tonar/issues)
160
+ 源码仓库:[https://github.com/sonion028/tonar](https://github.com/sonion028/tonar)
@@ -0,0 +1,3 @@
1
+ export default {
2
+ extends: ['@commitlint/config-conventional'],
3
+ };
package/cspell.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ // "import": ["@cspell/dict-lorem-ipsum/cspell-ext.json"], // 导入 乱数假文插件 字典
3
+ "caseSensitive": false, // 不区分大小写
4
+ "dictionaries": ["custom-dictionary"], // 自定义字典
5
+ "dictionaryDefinitions": [
6
+ {
7
+ "name": "custom-dictionary", // 自定义字典名称
8
+ "path": "./.cspell/custom-dictionary.txt", // 自定义字典文件路径
9
+ "addWords": true // 允许添加新单词到字典
10
+ }
11
+ ],
12
+ "ignorePaths": [
13
+ "**/node_modules/**",
14
+ "**/dist/**",
15
+ "**/build/**",
16
+ "**/public/**",
17
+ "**/lib/**",
18
+ "**/docs/**",
19
+ "**/vendor/**",
20
+ "**/static/**",
21
+ "**/out/**",
22
+ "**/tmp/**",
23
+ "**/stats.html",
24
+ "cspell.json",
25
+ ".cspell"
26
+ ]
27
+ }
@@ -0,0 +1,2 @@
1
+ import { i as e, n as t, r as n, t as r } from "./js/components.nFDoAkCq.js";
2
+ export { t as AsyncCustomShow, e as Carousel, n as CustomShow, r as ErrorBoundary };
@@ -0,0 +1 @@
1
+ ._carousel_1ryaf_1{width:100%;height:100%;position:relative;overflow:hidden}._carousel_1ryaf_1[style*=--wrapperWidth]{width:calc(var(--wrapperWidth) * 1px)}._carousel_1ryaf_1[style*=--wrapperHeight]{height:calc(var(--wrapperHeight) * 1px)}._carousel_1ryaf_1:not([style*=--cardHeight]) ._wrapper_1ryaf_13 ._inner_1ryaf_13{height:100%}._carousel_1ryaf_1[style*=--cardWidth] ._wrapper_1ryaf_13{width:calc(var(--cardWidth) * 1px)}._carousel_1ryaf_1[style*=--cardWidth] ._wrapper_1ryaf_13 ._inner_1ryaf_13{transform:translateX(calc(var(--index) * (var(--cardWidth) + var(--gapSize,0)) * -1px))}._carousel_1ryaf_1[style*=--cardWidth] ._wrapper_1ryaf_13 ._inner_1ryaf_13>*{flex:0 0 calc(var(--cardWidth) * 1px);height:100%}._carousel_1ryaf_1:not([style*=--cardWidth]) ._wrapper_1ryaf_13{width:100%}._carousel_1ryaf_1:not([style*=--cardWidth]) ._wrapper_1ryaf_13 ._inner_1ryaf_13{transform:translateX(calc((-100% + var(--gapSize,0) * -1px) * var(--index)))}._carousel_1ryaf_1:not([style*=--cardWidth]) ._wrapper_1ryaf_13 ._inner_1ryaf_13>*{flex:0 0 100%;height:100%}._carousel_1ryaf_1:hover>._arrow_1ryaf_36.hover{display:block}._carousel_1ryaf_1>._arrow_1ryaf_36.hover,._carousel_1ryaf_1>._arrow_1ryaf_36.none{display:none}._carousel_1ryaf_1>._arrow_1ryaf_36.always,._carousel_1ryaf_1>._arrow_1ryaf_36.auto{display:block}._carousel_1ryaf_1>._arrow_1ryaf_36 ._left_1ryaf_48,._carousel_1ryaf_1>._arrow_1ryaf_36 ._right_1ryaf_49{z-index:9;cursor:pointer;background:#fff url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMSAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgaWQ9ImRvd24iPgo8cGF0aCBpZD0iVW5pb24iIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNi43OTE4MiA1Ljc2ODY1QzYuNDAyNzMgNS4zNzk1NiA2LjQwMjczIDQuNzQ4NzEgNi43OTE4MiA0LjM1OTYyTDcuNDk2MzQgMy42NTUxQzcuODg1NDMgMy4yNjYwMSA4LjUxNjI4IDMuMjY2MDEgOC45MDUzNyAzLjY1NTFMMTQuNTQxNSA5LjI5MTI0QzE0LjczNzEgOS40ODY4MyAxNC44MzQ0IDkuNzQzNSAxNC44MzMzIDkuOTk5ODVDMTQuODM0NCAxMC4yNTYyIDE0LjczNzEgMTAuNTEyOSAxNC41NDE1IDEwLjcwODVMOC45MDUzNyAxNi4zNDQ2QzguNTE2MjggMTYuNzMzNyA3Ljg4NTQzIDE2LjczMzcgNy40OTYzNCAxNi4zNDQ2TDYuNzkxODIgMTUuNjQwMUM2LjQwMjczIDE1LjI1MSA2LjQwMjczIDE0LjYyMDEgNi43OTE4MiAxNC4yMzExTDExLjAyMyA5Ljk5OTg1TDYuNzkxODIgNS43Njg2NVoiIGZpbGw9IiMwMDY2RkMiLz4KPC9nPgo8L3N2Zz4K) 50%/contain no-repeat content-box border-box;border:1px solid #dde2e9;border-radius:50%;width:20px;height:20px;padding:14px;display:none;position:absolute;top:50%;transform:translateY(-50%);box-shadow:0 15px 35px -2px #0000000d,0 5px 15px #0000000d}._carousel_1ryaf_1>._arrow_1ryaf_36>.show{display:block}._carousel_1ryaf_1>._arrow_1ryaf_36 ._left_1ryaf_48{left:12px;transform:translateY(-50%)rotate(180deg)}._carousel_1ryaf_1>._arrow_1ryaf_36 ._right_1ryaf_49{right:12px}._carousel_1ryaf_1 ._wrapper_1ryaf_13{height:100%}._carousel_1ryaf_1 ._wrapper_1ryaf_13 ._inner_1ryaf_13{gap:calc(var(--gapSize) * 1px);height:calc(var(--cardHeight) * 1px);transition:transform .3s;display:flex}._indicator_1ryaf_85{z-index:9;border-radius:8px;gap:8px;display:flex;position:absolute;bottom:-17px;left:50%;transform:translate(-50%)}._indicator_1ryaf_85.none{display:none}._indicator_1ryaf_85.line>i{background-color:var(--Gray-3,#e5e8ef);cursor:pointer;border-radius:8px;width:40px;height:5px}._indicator_1ryaf_85.line>i.active{background:var(--byte-plus-blue-brand-primary-60,#0066fc)}._indicator_1ryaf_85.dot>i{background-color:var(--Gray-3,#e5e8ef);cursor:pointer;border-radius:50%;width:6px;height:6px}._indicator_1ryaf_85.dot>i.active{background:var(--byte-plus-blue-brand-primary-60,#0066fc)}