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.
- package/.cspell/custom-dictionary.txt +5 -0
- package/.github/actions/base-check/action.yaml +27 -0
- package/.github/actions/env-setup/action.yaml +74 -0
- package/.github/workflows/pr-check.yaml +33 -0
- package/.github/workflows/release.yaml +112 -0
- package/.gitignore +41 -0
- package/.node-version +1 -0
- package/.npmignore +7 -0
- package/.vscode/extensions.json +10 -0
- package/.vscode/settings.json +44 -0
- package/LICENSE +21 -0
- package/README.md +160 -0
- package/commitlint.config.ts +3 -0
- package/cspell.json +27 -0
- package/dist/components.esm.js +2 -0
- package/dist/css/components.C24nnsjt.css +1 -0
- package/dist/hooks.esm.js +185 -0
- package/dist/index.esm.js +4 -0
- package/dist/js/components.nFDoAkCq.js +198 -0
- package/dist/js/utils.CVb1iSAU.js +330 -0
- package/dist/types/components.d.ts +1 -0
- package/dist/types/hooks.d.ts +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/src/components/async-custom-show/index.d.ts +22 -0
- package/dist/types/src/components/carousel/hooks.d.ts +74 -0
- package/dist/types/src/components/carousel/index.d.ts +88 -0
- package/dist/types/src/components/custom-show/index.d.ts +21 -0
- package/dist/types/src/components/error-boundary/index.d.ts +31 -0
- package/dist/types/src/components/index.d.ts +8 -0
- package/dist/types/src/hooks/async-guard.d.ts +9 -0
- package/dist/types/src/hooks/index.d.ts +5 -0
- package/dist/types/src/hooks/observer.d.ts +70 -0
- package/dist/types/src/hooks/state.d.ts +44 -0
- package/dist/types/src/hooks/storage.d.ts +25 -0
- package/dist/types/src/hooks/timer.d.ts +16 -0
- package/dist/types/src/index.d.ts +3 -0
- package/dist/types/src/test/emiter-test.d.ts +1 -0
- package/dist/types/src/test/index.d.ts +1 -0
- package/dist/types/src/test/retry-async-test.d.ts +1 -0
- package/dist/types/src/utils/class-singleton.d.ts +6 -0
- package/dist/types/src/utils/concurrency.d.ts +17 -0
- package/dist/types/src/utils/debounce.d.ts +8 -0
- package/dist/types/src/utils/deep-copy.d.ts +11 -0
- package/dist/types/src/utils/dev.d.ts +8 -0
- package/dist/types/src/utils/download.d.ts +15 -0
- package/dist/types/src/utils/event-emitter/helpers.d.ts +36 -0
- package/dist/types/src/utils/event-emitter/index.d.ts +65 -0
- package/dist/types/src/utils/fetch-xhr/helpers.d.ts +28 -0
- package/dist/types/src/utils/fetch-xhr/index.d.ts +25 -0
- package/dist/types/src/utils/hash.d.ts +8 -0
- package/dist/types/src/utils/index.d.ts +15 -0
- package/dist/types/src/utils/is-deep-plain-equal.d.ts +15 -0
- package/dist/types/src/utils/json-convert.d.ts +66 -0
- package/dist/types/src/utils/micro-queue-scheduler.d.ts +14 -0
- package/dist/types/src/utils/raf-interval.d.ts +16 -0
- package/dist/types/src/utils/record-typed-map.d.ts +27 -0
- package/dist/types/src/utils/retry-async.d.ts +9 -0
- package/dist/types/src/utils/thenable.d.ts +15 -0
- package/dist/types/utils.d.ts +1 -0
- package/dist/utils.esm.js +2 -0
- package/eslint.config.ts +48 -0
- package/lint-staged.config.ts +13 -0
- package/oxfmt.config.ts +14 -0
- package/package.json +90 -0
- package/pnpm-workspace.yaml +3 -0
- package/scripts/sync-node-version/index.js +31 -0
- package/simple-git-hooks.js +4 -0
- package/src/components/async-custom-show/index.tsx +37 -0
- package/src/components/carousel/hooks.ts +312 -0
- package/src/components/carousel/index.module.scss +163 -0
- package/src/components/carousel/index.tsx +215 -0
- package/src/components/custom-show/index.tsx +31 -0
- package/src/components/error-boundary/index.tsx +48 -0
- package/src/components/index.ts +11 -0
- package/src/hooks/async-guard.ts +53 -0
- package/src/hooks/index.ts +5 -0
- package/src/hooks/observer.ts +236 -0
- package/src/hooks/state.ts +140 -0
- package/src/hooks/storage.ts +103 -0
- package/src/hooks/timer.ts +42 -0
- package/src/index.ts +3 -0
- package/src/test/emiter-test.ts +19 -0
- package/src/test/index.ts +35 -0
- package/src/test/retry-async-test.ts +8 -0
- package/src/utils/class-singleton.ts +23 -0
- package/src/utils/concurrency.ts +49 -0
- package/src/utils/debounce.ts +20 -0
- package/src/utils/deep-copy.ts +132 -0
- package/src/utils/dev.ts +16 -0
- package/src/utils/download.ts +39 -0
- package/src/utils/event-emitter/helpers.ts +80 -0
- package/src/utils/event-emitter/index.ts +171 -0
- package/src/utils/fetch-xhr/helpers.ts +85 -0
- package/src/utils/fetch-xhr/index.ts +103 -0
- package/src/utils/hash.ts +25 -0
- package/src/utils/index.ts +18 -0
- package/src/utils/is-deep-plain-equal.ts +45 -0
- package/src/utils/json-convert.ts +257 -0
- package/src/utils/micro-queue-scheduler.ts +38 -0
- package/src/utils/raf-interval.ts +42 -0
- package/src/utils/record-typed-map.ts +38 -0
- package/src/utils/retry-async.ts +30 -0
- package/src/utils/thenable.ts +30 -0
- package/tsconfig.json +43 -0
- package/types/scss.d.ts +10 -0
- package/vite.config.ts +51 -0
|
@@ -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,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)
|
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 @@
|
|
|
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)}
|