diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..f3b1a9f --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN} diff --git a/CHANGELOG.md b/CHANGELOG.md index 825fd95..f999df4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,12 @@ # CHANGELOG -## v1.0.0 (2026-05-29) +## v1.0.2 (2026-06-12) +- **架构对齐**: 遵循 `base` 包新设计,彻底消灭用户侧 ESM 强依赖。 +- **组件注册**: 支持 `` 全局组件声明式用法。 +- **构建优化**: 产物调整为纯 UMD 模式,通过 `globalThis.DataChart` 暴露 API。 +## v1.0.0 (2026-05-29) +... ### 核心特性 - **ESM 封装**: 完成对 Chart.js 4.x 的原生 ESM 封装。 - **DataChart 组件**: 实现声明式图表初始化与数据驱动更新。 diff --git a/README.md b/README.md index ba3fb89..9bed55f 100644 --- a/README.md +++ b/README.md @@ -1,80 +1,61 @@ -# Chart.js ESM 封装层 (@web/chart) +# @apigo.cc/chart API 手册 -基于 **Chart.js 4.x** 的原生 ESM 封装,专为数据驱动的 UI 架构设计。支持“用户侧零打包”引入,通过 `DataChart` 类实现高性能、响应式的图表渲染。 +基于 Chart.js 4.x 的轻量级图表封装库。内置数据映射(Data Mapping)功能。 -## 1. 核心设计原则 -- **原生 ESM**: 源码与产物均为标准 ESM 格式。 -- **数据驱动**: 状态变更时调用 `update(data)`,严禁频繁销毁/重建实例。 -- **极致性能**: 自动处理组件生命周期,内置 Chart.js 核心控制器。 +--- -## 2. 快速开始 +## 1. 引入方式 (UMD 优先) -### 2.1 引入方式 -推荐通过 `importmap` 或 `loader.js` 引入: +在 HTML 中引入脚本即可,组件已内置 Chart.js,无需额外依赖。 ```html - + + ``` -### 2.2 基础用法 +> **ESM 模式说明**:如需在模块化环境中使用,引入 `dist/chart.min.mjs`。 -```javascript -import { DataChart } from '@web/chart' +--- -const canvas = document.getElementById('myChart') -const chart = new DataChart(canvas, { - type: 'line', - data: { - labels: ['Q1', 'Q2', 'Q3', 'Q4'], - datasets: [{ - label: '销售额', - data: [12, 19, 3, 5] - }] - }, - options: { - responsive: true - } -}) +## 2. 基本用法 -// 响应式更新数据 -chart.update({ - labels: ['Q1', 'Q2', 'Q3', 'Q4'], - datasets: [{ - label: '销售额', - data: [15, 25, 10, 8] - }] -}) +### 组件化用法 (推荐) +直接在 HTML 中使用 `` 标签,通过 `state` 进行数据绑定。 + +```html +
+ +
``` +### JS 调用方式 (兼容) +```html + +... +--- + ## 3. API 参考 -### `DataChart(canvas: HTMLCanvasElement, config: Object)` -构造函数,初始化图表。 -- `config.type`: 图表类型 (`'line'`, `'bar'`, `'pie'`, `'doughnut'`)。 -- `config.data`: Chart.js 标准数据格式。 -- `config.options`: Chart.js 标准配置。 +### `new DataChart(canvas, config)` +- **`canvas`**: HTMLCanvasElement。 +- **`config.type`**: `'line'`, `'bar'`, `'pie'`。 +- **`config.data`**: 原始对象数组。 +- **`config.map`**: 映射规则。 +- **`config.options`**: 透传给底层 Chart.js 的原生配置。 -### `update(data?: Object)` -平滑更新图表。 -- `data`: (可选) 新的数据对象。若不传,则强制触发现有数据的重绘。 +### 实例方法 +- **`update(newData)`**: 传入新数组重绘图表。 +- **`destroy()`**: 销毁实例。 -### `destroy()` -销毁图表实例,释放内存。 +--- -## 4. 支持的图表类型 -内置集成了以下控制器,无需额外注册: -- Line (折线图) -- Bar (柱状图) -- Pie / Doughnut (饼图/环形图) -- Tooltip & Legend 插件 - -## 5. 开发与测试 -- **测试靶场**: `test/index.html` (支持热更新调试) -- **单元测试**: `npm run test` (基于 Playwright) -- **构建**: `npm run build` (产出 `chart.js` 与 `chart.min.js`) +## 开发者提示 (AI 必读) +1. **尺寸控制**: Canvas 必须具有明确的高度和宽度。 +2. **全局变量**: UMD 模式下,`DataChart` 类自动挂载到 `window`。 diff --git a/TEST.md b/TEST.md index 689b5b0..56d1e74 100644 --- a/TEST.md +++ b/TEST.md @@ -5,13 +5,14 @@ - CPU: Host machine - Chart.js version: 4.x -## 性能基准 (v1.0.0) +## 性能基准 (v1.0.2) | 指标 | 耗时 (ms) | 备注 | | :--- | :--- | :--- | -| **首次渲染 (Line Chart)** | 43.80 | 包含 Chart.js 初始化与双图表渲染测试 | +| **首次渲染 (Line Chart)** | 44.50 | 包含 Chart.js 初始化、双图表渲染及组件初始化测试 | | **数据更新 (Smooth Update)** | < 10.00 | 基于 `chart.update()` 的增量更新 | ## 设计决策 1. **Tree Shaking**: 仅内置常用的控制器(Line, Bar, Pie),以控制产物体积。 2. **实例复用**: 严格遵循 `chart.update()` 路径,避免频繁创建 Canvas 渲染上下文。 +3. **组件化支持**: 通过 `globalThis.Component` 注册,实现零打包声明式渲染。 diff --git a/package-lock.json b/package-lock.json index 25bb632..2aadbad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "@web/chart", + "name": "@apigo.cc/chart", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "@web/chart", + "name": "@apigo.cc/chart", "version": "1.0.0", "dependencies": { "chart.js": "^4.4.0" diff --git a/package.json b/package.json index 3195ddc..33d6e39 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,8 @@ { "name": "@apigo.cc/chart", - "version": "1.0.1", + "version": "1.0.2", "type": "module", "main": "dist/chart.js", - "module": "dist/chart.js", "files": [ "dist" ], diff --git a/playwright.config.js b/playwright.config.js index 2605e63..7391ebe 100644 --- a/playwright.config.js +++ b/playwright.config.js @@ -4,11 +4,11 @@ export default defineConfig({ testDir: './test', testMatch: '**/*.spec.js', use: { - baseURL: 'http://localhost:8082', + baseURL: 'http://127.0.0.1:8085', }, webServer: { - command: 'npx vite --port 8082', - url: 'http://localhost:8082', + command: 'npx vite --port 8085 --strictPort --host 127.0.0.1', + url: 'http://127.0.0.1:8085', timeout: 120000, reuseExistingServer: !process.env.CI, }, diff --git a/scripts/publish.js b/scripts/publish.js new file mode 100644 index 0000000..b562aa9 --- /dev/null +++ b/scripts/publish.js @@ -0,0 +1,48 @@ +import { execSync } from 'child_process'; +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +try { + // 1. 获取最新 tag + let tag; + try { + tag = execSync('git describe --tags --abbrev=0', { encoding: 'utf8' }).trim(); + } catch (err) { + throw new Error('Failed to find git tags. Please make sure the repository has tags (e.g., v1.0.0) before publishing.'); + } + // 去掉 v 前缀 + const version = tag.startsWith('v') ? tag.slice(1) : tag; + + console.log(`Latest git tag: ${tag}, Version to publish: ${version}`); + + // 2. 读取并更新 package.json + const pkgPath = path.join(__dirname, '../package.json'); + const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); + + // 保持原有名称(如果已经带有 @apigo.cc/ 前缀)或替换前缀 + if (!pkg.name.startsWith('@apigo.cc/')) { + const baseName = pkg.name.includes('/') ? pkg.name.split('/')[1] : pkg.name; + pkg.name = `@apigo.cc/${baseName}`; + } + pkg.version = version; + + fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n'); + console.log(`Updated package.json: name=${pkg.name}, version=${pkg.version}`); + + // 3. 构建 + console.log('Running build...'); + execSync('npm run build', { stdio: 'inherit', cwd: path.join(__dirname, '..') }); + + // 4. 发布 + console.log('Publishing to npm...'); + const args = process.argv.slice(2).join(' '); + execSync(`npm publish --access public ${args}`, { stdio: 'inherit', cwd: path.join(__dirname, '..') }); + + console.log('Publish successful!'); +} catch (error) { + console.error('Publish failed:', error.message); + process.exit(1); +} diff --git a/src/index.js b/src/index.js index f2d87c0..6f83284 100644 --- a/src/index.js +++ b/src/index.js @@ -76,3 +76,34 @@ export class DataChart { } } } + +// 注册为全局组件 +if (typeof globalThis !== 'undefined' && globalThis.Component) { + globalThis.Component.register('DataChart', container => { + const canvas = container.querySelector('canvas'); + let dc = null; + const init = () => { + if (dc) dc.destroy(); + dc = new DataChart(canvas, { + type: container.getAttribute('type') || 'line', + data: container.state.data, + options: container.state.options, + map: container.state.map + }); + container.chartInstance = dc; + }; + + container.state.__watch('data', (val) => dc ? dc.update(val) : init()); + container.state.__watch('options', init); + container.state.__watch('map', init); + + init(); + + container.addEventListener('unload', () => dc && dc.destroy()); + }, globalThis.Util?.makeDom('')); +} + +// 挂载到全局 +if (typeof globalThis !== 'undefined') { + globalThis.DataChart = DataChart; +} diff --git a/test/index.html b/test/index.html index a2ce826..9e9a005 100644 --- a/test/index.html +++ b/test/index.html @@ -5,7 +5,7 @@ @@ -21,13 +21,37 @@
+
+

Component Test

+
+ +
+