transone-chart 0.1.2
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 +181 -0
- package/dist/adapters/index.d.ts +7 -0
- package/dist/adapters/miniprogram.d.ts +44 -0
- package/dist/adapters/native.d.ts +28 -0
- package/dist/adapters/types.d.ts +27 -0
- package/dist/adapters/web.d.ts +13 -0
- package/dist/charts/bar.d.ts +25 -0
- package/dist/charts/index.d.ts +4 -0
- package/dist/charts/line.d.ts +20 -0
- package/dist/charts/pie.d.ts +14 -0
- package/dist/charts/radar.d.ts +16 -0
- package/dist/component.d.ts +41 -0
- package/dist/core/axis.d.ts +47 -0
- package/dist/core/canvas.d.ts +61 -0
- package/dist/core/chart.d.ts +69 -0
- package/dist/core/layout.d.ts +58 -0
- package/dist/core/legend.d.ts +26 -0
- package/dist/core/scale.d.ts +67 -0
- package/dist/core/text.d.ts +20 -0
- package/dist/factory.d.ts +22 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +49 -0
- package/dist/types.d.ts +180 -0
- package/lib/adapters/index.ts +18 -0
- package/lib/adapters/miniprogram.ts +146 -0
- package/lib/adapters/native.ts +39 -0
- package/lib/adapters/types.ts +44 -0
- package/lib/adapters/web.ts +41 -0
- package/lib/charts/bar.ts +270 -0
- package/lib/charts/index.ts +4 -0
- package/lib/charts/line.ts +153 -0
- package/lib/charts/pie.ts +110 -0
- package/lib/charts/radar.ts +166 -0
- package/lib/component.ts +149 -0
- package/lib/core/axis.ts +174 -0
- package/lib/core/canvas.ts +130 -0
- package/lib/core/chart.ts +322 -0
- package/lib/core/layout.ts +221 -0
- package/lib/core/legend.ts +94 -0
- package/lib/core/scale.ts +183 -0
- package/lib/core/text.ts +70 -0
- package/lib/factory.ts +62 -0
- package/lib/index.ts +69 -0
- package/lib/types.ts +218 -0
- package/package.json +55 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../lib/types.ts", "../lib/core/canvas.ts", "../lib/core/scale.ts", "../lib/core/layout.ts", "../lib/core/text.ts", "../lib/core/axis.ts", "../lib/core/legend.ts", "../lib/core/chart.ts", "../lib/charts/line.ts", "../lib/charts/bar.ts", "../lib/charts/pie.ts", "../lib/charts/radar.ts", "../lib/adapters/types.ts", "../lib/adapters/miniprogram.ts", "../lib/adapters/web.ts", "../lib/factory.ts", "../lib/adapters/native.ts", "../../transone/lib/core/vnode.ts", "../../transone/lib/core/reactive/types.ts", "../../transone/lib/core/reactive/scheduler.ts", "../../transone/lib/core/reactive.ts", "../../transone/lib/core/model.ts", "../../transone/lib/style/units.ts", "../../transone/lib/core/renderer/props.ts", "../../transone/lib/style/StyleManager.ts", "../../transone/lib/core/template.ts", "../../transone/lib/core/component/base.ts", "../../transone/lib/router/instance.ts", "../../transone/lib/core/renderer/element-strategy.ts", "../../transone/lib/core/renderer.ts", "../../transone/lib/request/errors.ts", "../../transone/lib/request/helpers.ts", "../../transone/lib/request/adapters/fetch.ts", "../../transone/lib/request/adapters/platform.ts", "../../transone/lib/request/adapters/mp.ts", "../../transone/lib/request/adapters/index.ts", "../../transone/lib/request/interceptors.ts", "../../transone/lib/request/request.ts", "../../transone/lib/request/index.ts", "../lib/component.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * transone-chart 公共类型定义。\n *\n * 所有图表共用一份 Option 风格配置(对齐 ECharts 心智:xAxis / yAxis /\n * series / legend / title),但实现零依赖、纯 Canvas 2D 绘制。\n */\n\n/** 一期支持的图表类型。 */\nexport type ChartType = 'line' | 'bar' | 'pie' | 'radar';\n\n/** 默认主题色板(与 transone-ui 主色一致,可按需覆盖)。 */\nexport const DEFAULT_PALETTE: readonly string[] = [\n '#1677ff',\n '#00b578',\n '#ff8f1f',\n '#ff3141',\n '#eb2f96',\n '#722ed1',\n '#13c2c2',\n '#faad14',\n '#2f54eb',\n '#a0d911',\n];\n\nexport const DEFAULT_TEXT_COLOR = '#323233';\nexport const DEFAULT_AXIS_COLOR = '#c8c9cc';\nexport const DEFAULT_GRID_COLOR = '#ebedf0';\nexport const DEFAULT_FONT_FAMILY =\n \"-apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Microsoft YaHei', sans-serif\";\n\n/** 标题配置。 */\nexport interface TitleOption {\n text: string;\n fontSize?: number;\n color?: string;\n /** 标题与绘图区之间的留白,默认 16。 */\n padding?: number;\n}\n\n/** 图例配置。 */\nexport interface LegendOption {\n show?: boolean;\n /** 图例位置:顶部横向 / 底部横向 / 右侧纵向。 */\n position?: 'top' | 'bottom' | 'right';\n fontSize?: number;\n color?: string;\n /** 图例色块边长(正方形),默认 12。 */\n markerSize?: number;\n /** 图例项之间的水平间距,默认 16。 */\n itemGap?: number;\n}\n\n/** 数值轴(y 轴/雷达数值)配置。 */\nexport interface ValueAxisOption {\n /** 显式最小值;缺省自动 nice。 */\n min?: number;\n /** 显式最大值;缺省自动 nice。 */\n max?: number;\n /** 刻度分段数,默认 5。 */\n splitCount?: number;\n labelFontSize?: number;\n labelColor?: string;\n gridColor?: string;\n gridLineWidth?: number;\n /** 网格虚线,如 [4, 4];默认实线。 */\n gridLineDash?: number[];\n /** 是否显示网格,默认 true。 */\n showGrid?: boolean;\n /** 刻度标签格式化。 */\n format?: (value: number) => string;\n}\n\n/** 类目轴(x 轴/雷达指标)配置。 */\nexport interface CategoryAxisOption {\n labels: readonly string[];\n labelFontSize?: number;\n labelColor?: string;\n gridColor?: string;\n gridLineWidth?: number;\n /** 是否显示类目网格竖线,默认 false。 */\n showGrid?: boolean;\n}\n\n/** —— 折线图 —— */\n\nexport interface LineSeries {\n name?: string;\n data: readonly number[];\n color?: string;\n /** 平滑曲线(三次贝塞尔插值),默认 false。 */\n smooth?: boolean;\n /** 是否填充系列与数值轴之间的面积,默认 false。 */\n area?: boolean;\n /** 是否绘制数据点标记,默认 true。 */\n showSymbol?: boolean;\n lineWidth?: number;\n}\n\nexport interface LineChartOption {\n type: 'line';\n title?: TitleOption;\n legend?: LegendOption;\n xAxis: CategoryAxisOption;\n yAxis?: ValueAxisOption;\n series: readonly LineSeries[];\n /** 数值轴是否从 0 开始(数据全为正时强制含 0),默认 false(按数据范围紧凑显示)。 */\n startFromZero?: boolean;\n /** 背景色(如 'rgba(22,119,255,0.06)'),默认无。 */\n backgroundColor?: string;\n}\n\n/** —— 柱状图 —— */\n\nexport interface BarSeries {\n name?: string;\n data: readonly number[];\n color?: string;\n /** 堆叠组名;同名系列纵向堆叠。 */\n stack?: string;\n /** 柱体宽度(px),缺省按类目带自动计算。 */\n barWidth?: number;\n /** 柱体圆角,默认 0。 */\n borderRadius?: number;\n}\n\nexport interface BarChartOption {\n type: 'bar';\n title?: TitleOption;\n legend?: LegendOption;\n xAxis: CategoryAxisOption;\n yAxis?: ValueAxisOption;\n series: readonly BarSeries[];\n /** 横向柱状图(类目轴转纵向、数值轴转横向),默认 false。 */\n horizontal?: boolean;\n backgroundColor?: string;\n}\n\n/** —— 饼图 —— */\n\nexport interface PieDatum {\n name: string;\n value: number;\n color?: string;\n}\n\nexport interface PieChartOption {\n type: 'pie';\n data: readonly PieDatum[];\n title?: TitleOption;\n legend?: LegendOption;\n /** 外半径:数字(px)或百分比字符串(相对 min(width, height) / 2),默认 '60%'。 */\n radius?: number | string;\n /** 内半径:0 为饼图;>0 为环形图,默认 0。 */\n innerRadius?: number | string;\n /** 是否显示扇区标签(名称 + 百分比),默认 true。 */\n showLabel?: boolean;\n labelFontSize?: number;\n labelColor?: string;\n /** 起始角(弧度),默认 -Math.PI / 2(12 点方向),顺时针。 */\n startAngle?: number;\n backgroundColor?: string;\n}\n\n/** —— 雷达图 —— */\n\nexport interface RadarIndicator {\n name: string;\n /** 指标最大值;缺省取所有系列该指标的最大值。 */\n max?: number;\n}\n\nexport interface RadarSeries {\n name?: string;\n data: readonly number[];\n color?: string;\n /** 是否填充多边形区域,默认 true。 */\n area?: boolean;\n lineWidth?: number;\n}\n\nexport interface RadarChartOption {\n type: 'radar';\n indicators: readonly RadarIndicator[];\n series: readonly RadarSeries[];\n title?: TitleOption;\n legend?: LegendOption;\n /** 网格层数,默认 5。 */\n splitCount?: number;\n gridColor?: string;\n gridLineWidth?: number;\n labelFontSize?: number;\n labelColor?: string;\n /** 雷达中心半径(px),缺省取 min(width, height) / 2 的 60%。 */\n radius?: number;\n /** 起始角(弧度),默认 -Math.PI / 2(12 点方向),顺时针。 */\n startAngle?: number;\n backgroundColor?: string;\n}\n\nexport type ChartOption =\n | LineChartOption\n | BarChartOption\n | PieChartOption\n | RadarChartOption;\n\n/** 图表渲染上下文(适配器解析后的产物)。 */\nexport interface ChartRenderContext {\n /** 跨端 Canvas 2D 上下文。 */\n ctx: import('./core/canvas').ICanvas2D;\n /** 逻辑宽度(CSS px)。 */\n width: number;\n /** 逻辑高度(CSS px)。 */\n height: number;\n /** 设备像素比。 */\n dpr: number;\n /** 自定义色板,缺省使用 DEFAULT_PALETTE。 */\n palette?: readonly string[];\n}\n",
|
|
6
|
+
"/**\n * 跨端 Canvas 2D 契约(ICanvas2D)。\n *\n * 这是 transone-chart 与平台解耦的唯一边界:\n * - Web:HTMLCanvasElement.getContext('2d') 天然满足,适配器薄封装\n * - 小程序:微信 / 阿里 / 字节的 Canvas 2D(type=\"2d\")节点上下文与 Web 接口基本对齐\n * - 未来原生 App:Skia / ArkUI 等原生 canvas 只需实现本接口即可接入同一套引擎\n *\n * 接口刻意收敛为图表实际需要的最小真子集,避免依赖平台各自的扩展 API;\n * 绘制一律使用「beginPath → 路径命令 → fill()/stroke() 无参」形式,\n * 不依赖 Path2D 等小程序端可能缺失的高级对象。\n */\n\nexport interface IGradient {\n addColorStop(offset: number, color: string): void;\n}\n\nexport type CanvasLineCap = 'butt' | 'round' | 'square';\nexport type CanvasLineJoin = 'bevel' | 'round' | 'miter';\nexport type CanvasTextAlign =\n | 'left'\n | 'right'\n | 'center'\n | 'start'\n | 'end';\nexport type CanvasTextBaseline =\n | 'top'\n | 'hanging'\n | 'middle'\n | 'alphabetic'\n | 'ideographic'\n | 'bottom';\n\nexport interface ICanvas2D {\n /* —— 状态管理 —— */\n save(): void;\n restore(): void;\n translate(x: number, y: number): void;\n scale(x: number, y: number): void;\n rotate(angle: number): void;\n\n /* —— 样式属性 —— */\n fillStyle: string | IGradient;\n strokeStyle: string | IGradient;\n lineWidth: number;\n lineCap: CanvasLineCap;\n lineJoin: CanvasLineJoin;\n globalAlpha: number;\n font: string;\n textAlign: CanvasTextAlign;\n textBaseline: CanvasTextBaseline;\n\n /* —— 路径 —— */\n beginPath(): void;\n closePath(): void;\n moveTo(x: number, y: number): void;\n lineTo(x: number, y: number): void;\n bezierCurveTo(\n cp1x: number,\n cp1y: number,\n cp2x: number,\n cp2y: number,\n x: number,\n y: number\n ): void;\n arc(\n x: number,\n y: number,\n radius: number,\n startAngle: number,\n endAngle: number,\n counterclockwise?: boolean\n ): void;\n arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): void;\n rect(x: number, y: number, width: number, height: number): void;\n ellipse(\n x: number,\n y: number,\n radiusX: number,\n radiusY: number,\n rotation: number,\n startAngle: number,\n endAngle: number,\n counterclockwise?: boolean\n ): void;\n\n /* —— 绘制 —— */\n fill(): void;\n stroke(): void;\n fillRect(x: number, y: number, width: number, height: number): void;\n clearRect(x: number, y: number, width: number, height: number): void;\n clip(): void;\n setLineDash(segments: readonly number[]): void;\n\n /* —— 渐变 —— */\n createLinearGradient(\n x0: number,\n y0: number,\n x1: number,\n y1: number\n ): IGradient;\n createRadialGradient(\n x0: number,\n y0: number,\n r0: number,\n x1: number,\n y1: number,\n r1: number\n ): IGradient;\n\n /* —— 文字 —— */\n fillText(text: string, x: number, y: number, maxWidth?: number): void;\n measureText(text: string): { width: number };\n}\n\n/**\n * ICanvas2D 的便捷判断:任意对象只要实现了最小方法集即视为满足契约。\n * 用于适配器防御与测试 mock 的类型断言,不承担运行时校验。\n */\nexport function isCanvas2DLike(value: unknown): value is ICanvas2D {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as ICanvas2D).save === 'function' &&\n typeof (value as ICanvas2D).beginPath === 'function' &&\n typeof (value as ICanvas2D).fill === 'function' &&\n typeof (value as ICanvas2D).stroke === 'function' &&\n typeof (value as ICanvas2D).fillText === 'function'\n );\n}\n",
|
|
7
|
+
"/**\n * 比例尺:把数据域(domain)映射到像素范围(range)。\n *\n * - LinearScale:数值 → 像素,自动 nice 刻度(0.5 / 1 / 2 / 5 步进)\n * - CategoryScale:类目索引 → 像素带(band),供柱状图计算柱宽\n *\n * 纯函数与轻量类实现,零平台依赖,便于单元测试。\n */\n\n/** 数值域映射接口。 */\nexport interface LinearScaleOptions {\n /** 数据最小值(未指定则按数据自动 nice)。 */\n min?: number;\n /** 数据最大值(未指定则按数据自动 nice)。 */\n max?: number;\n /** 期望刻度分段数,默认 5。 */\n splitCount?: number;\n}\n\nexport interface ScaleResult {\n /** 映射后的像素坐标(已夹取到 range 内)。 */\n scale(value: number): number;\n /** nice 后的实际最小值。 */\n min: number;\n /** nice 后的实际最大值。 */\n max: number;\n /** 刻度值数组(含 min/max)。 */\n ticks: number[];\n}\n\n/**\n * 计算 nice 刻度:把任意 [min, max] 数据范围规整为\"美观\"范围与刻度。\n * 步进从 1 / 2 / 5 × 10^n 中选择,保证刻度间距整齐。\n */\nexport function niceTicks(\n rawMin: number,\n rawMax: number,\n splitCount = 5\n): { min: number; max: number; step: number; ticks: number[] } {\n let min = rawMin;\n let max = rawMax;\n\n if (!Number.isFinite(min) || !Number.isFinite(max)) {\n throw new Error('niceTicks: domain must be finite numbers');\n }\n if (min === max) {\n // 常量数据:人为撑开一个单位区间,避免除以零\n const pad = Math.abs(min) > 1 ? Math.abs(min) * 0.1 : 1;\n min -= pad;\n max += pad;\n }\n if (min > max) {\n [min, max] = [max, min];\n }\n\n const span = max - min;\n const roughStep = span / Math.max(1, splitCount);\n const step = niceStep(roughStep);\n\n const niceMin = Math.floor(min / step) * step;\n const niceMax = Math.ceil(max / step) * step;\n\n const ticks: number[] = [];\n for (let v = niceMin; v <= niceMax + step * 1e-9; v += step) {\n // 浮点误差收敛:四舍五入到 step 的小数位数\n ticks.push(roundToStep(v, step));\n }\n\n return { min: niceMin, max: niceMax, step, ticks };\n}\n\nfunction niceStep(rough: number): number {\n const power = Math.pow(10, Math.floor(Math.log10(rough)));\n const fraction = rough / power;\n let niceFraction: number;\n if (fraction <= 1) {\n niceFraction = 1;\n } else if (fraction <= 2) {\n niceFraction = 2;\n } else if (fraction <= 5) {\n niceFraction = 5;\n } else {\n niceFraction = 10;\n }\n return niceFraction * power;\n}\n\nfunction roundToStep(value: number, step: number): number {\n const decimals = Math.max(0, -Math.floor(Math.log10(step)) + 1);\n return Number(value.toFixed(decimals));\n}\n\n/** 线性比例尺:domain → [rangeStart, rangeEnd],自动 nice。 */\nexport class LinearScale {\n public readonly min: number;\n public readonly max: number;\n public readonly ticks: number[];\n public readonly step: number;\n\n private readonly rangeStart: number;\n private readonly rangeEnd: number;\n\n public constructor(\n dataMin: number,\n dataMax: number,\n rangeStart: number,\n rangeEnd: number,\n options: LinearScaleOptions = {}\n ) {\n const { min: explicitMin, max: explicitMax, splitCount = 5 } = options;\n\n let min = explicitMin;\n let max = explicitMax;\n\n if (min === undefined || max === undefined) {\n const auto = niceTicks(dataMin, dataMax, splitCount);\n min = min ?? auto.min;\n max = max ?? auto.max;\n this.ticks = auto.ticks;\n this.step = auto.step;\n } else {\n this.step = (max - min) / Math.max(1, splitCount);\n this.ticks = [];\n for (let i = 0; i <= splitCount; i += 1) {\n this.ticks.push(min + i * this.step);\n }\n }\n\n this.min = min;\n this.max = max;\n this.rangeStart = rangeStart;\n this.rangeEnd = rangeEnd;\n }\n\n /** 数值 → 像素坐标(反向轴传 rangeEnd > rangeStart 即自然反转)。 */\n public scale(value: number): number {\n const ratio = (value - this.min) / (this.max - this.min);\n return this.rangeStart + ratio * (this.rangeEnd - this.rangeStart);\n }\n}\n\n/** 类目比例尺:N 个类目 → range 内均分带(band)。 */\nexport class CategoryScale {\n /** 每个类目带的像素宽度。 */\n public readonly bandWidth: number;\n /** 类目带之间的间距占比(0~1),默认 0.35。 */\n public readonly innerGapRatio: number;\n\n private readonly rangeStart: number;\n\n public constructor(\n public readonly categories: readonly unknown[],\n rangeStart: number,\n rangeEnd: number,\n innerGapRatio = 0.35\n ) {\n this.innerGapRatio = innerGapRatio;\n this.rangeStart = rangeStart;\n const count = Math.max(1, categories.length);\n this.bandWidth = (rangeEnd - rangeStart) / count;\n }\n\n /** 第 index 个类目的带起始(绝对像素坐标)。 */\n public bandStart(index: number): number {\n return this.rangeStart + this.bandWidth * index;\n }\n\n /** 第 index 个类目的带中心(绝对像素坐标)。 */\n public center(index: number): number {\n return this.bandStart(index) + this.bandWidth / 2;\n }\n\n /** 可用绘图宽度 = 带宽 × (1 - innerGapRatio)。 */\n public innerWidth(): number {\n return this.bandWidth * (1 - this.innerGapRatio);\n }\n\n /** 每组(多系列)子柱宽度:按系列数均分可用宽度,并预留 20% 间隙。 */\n public groupInnerWidth(groupCount: number): number {\n const usable = this.innerWidth();\n return usable / Math.max(1, groupCount) * 0.8;\n }\n}\n",
|
|
8
|
+
"/**\n * 图表布局计算:在给定画布尺寸内,按 title / legend / 坐标轴标签区\n * 逐层扣除,得到最终绘图区(plot)。\n *\n * 布局只依赖注入的文本测量函数,不接触 Canvas 上下文本身,\n * 保持纯逻辑可测。\n */\n\nimport type { LegendOption, TitleOption } from '../types';\n\nexport interface Box {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface Padding {\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\n\nexport interface LayoutInput {\n width: number;\n height: number;\n title?: TitleOption;\n /** 图例配置(已解析 show/position)。 */\n legend?: LegendOption;\n /** 图例项名称列表(用于测量总宽度)。 */\n legendItems?: readonly string[];\n /** 数值轴刻度标签(测量最大宽度)。 */\n valueLabels?: readonly string[];\n /** 类目轴标签(测量最大宽度/高度)。 */\n categoryLabels?: readonly string[];\n /** 横向布局(横向柱状图):类目轴占左侧、数值轴占底部。 */\n horizontal?: boolean;\n /** 基础内边距,默认 { top: 12, right: 16, bottom: 12, left: 16 }。 */\n basePadding?: Partial<Padding>;\n}\n\nexport interface LayoutResult {\n /** 绘图区(含网格与数据系列)。 */\n plot: Box;\n /** 数值轴标签区(横轴图中在左侧,横向图中在底部)。 */\n valueAxisBox: Box;\n /** 类目轴标签区(横轴图中在底部,横向图中在左侧)。 */\n categoryAxisBox: Box;\n /** 标题占用区(若存在)。 */\n titleBox: Box | null;\n /** 图例占用区(若存在)。 */\n legendBox: Box | null;\n padding: Padding;\n}\n\nexport type MeasureTextFn = (text: string) => number;\n\nconst DEFAULT_PADDING: Padding = { top: 12, right: 16, bottom: 12, left: 16 };\nconst TITLE_PADDING = 16;\nconst AXIS_LABEL_PADDING = 8;\nconst LEGEND_PADDING = 12;\n\n/** 计算图例占用的行高(top/bottom 单行)或列宽(right 纵向)。 */\nfunction measureLegend(\n input: LayoutInput,\n measure: MeasureTextFn\n): { width: number; height: number } {\n const { legend } = input;\n const items = input.legendItems ?? [];\n const fontSize = legend?.fontSize ?? 12;\n const itemGap = legend?.itemGap ?? 16;\n const markerSize = legend?.markerSize ?? 12;\n\n if (legend?.position === 'right') {\n // 纵向:宽度 = 最宽项(色块 + 间距 + 文本),高度 = 项数 × 行高\n let maxWidth = 0;\n for (const item of items) {\n const w = markerSize + 6 + measure(item);\n if (w > maxWidth) {\n maxWidth = w;\n }\n }\n return {\n width: maxWidth + LEGEND_PADDING,\n height: Math.max(items.length * (fontSize + 8) + LEGEND_PADDING, 0),\n };\n }\n\n // 横向:单行居中,总宽 = 各项宽之和\n let totalWidth = 0;\n for (const item of items) {\n totalWidth += markerSize + 6 + measure(item) + itemGap;\n }\n if (totalWidth > 0) {\n totalWidth -= itemGap;\n }\n return { width: totalWidth, height: fontSize + LEGEND_PADDING + 4 };\n}\n\n/**\n * 计算完整布局。规则:\n * - title 固定顶部\n * - legend:top → title 之下;bottom → 底部;right → 最右侧\n * - 数值轴标签区与类目轴标签区按 horizontal 互换左右/底部\n */\nexport function computeLayout(\n input: LayoutInput,\n measure: MeasureTextFn\n): LayoutResult {\n const padding: Padding = { ...DEFAULT_PADDING, ...input.basePadding };\n\n let top = padding.top;\n let bottom = padding.bottom;\n let left = padding.left;\n let right = padding.right;\n\n let titleBox: Box | null = null;\n if (input.title?.text) {\n const fontSize = input.title.fontSize ?? 16;\n titleBox = { x: 0, y: 0, width: input.width, height: fontSize };\n top += fontSize + (input.title.padding ?? TITLE_PADDING);\n }\n\n let legendBox: Box | null = null;\n const legend = input.legend;\n if (legend && legend.show !== false && (input.legendItems?.length ?? 0) > 0) {\n const size = measureLegend(input, measure);\n const position = legend.position ?? 'top';\n if (position === 'top') {\n legendBox = {\n x: 0,\n y: titleBox ? titleBox.height : 0,\n width: input.width,\n height: size.height,\n };\n top += size.height;\n } else if (position === 'bottom') {\n legendBox = {\n x: 0,\n y: input.height - size.height,\n width: input.width,\n height: size.height,\n };\n bottom += size.height;\n } else {\n legendBox = {\n x: input.width - size.width,\n y: top,\n width: size.width,\n height: Math.max(0, input.height - top - bottom),\n };\n right += size.width;\n }\n }\n\n // 坐标轴标签区\n const labelFontSize = 11;\n let valueAxisWidth = 0;\n let categoryAxisHeight = 0;\n let valueAxisHeight = 0;\n let categoryAxisWidth = 0;\n\n const valueLabels = input.valueLabels ?? [];\n const categoryLabels = input.categoryLabels ?? [];\n\n if (input.horizontal) {\n // 横向图:类目轴在左侧(列宽),数值轴在底部(行高)\n if (categoryLabels.length > 0) {\n for (const label of categoryLabels) {\n const w = measure(label);\n if (w > categoryAxisWidth) {\n categoryAxisWidth = w;\n }\n }\n categoryAxisWidth += AXIS_LABEL_PADDING * 2;\n left += categoryAxisWidth;\n }\n if (valueLabels.length > 0) {\n valueAxisHeight = labelFontSize + AXIS_LABEL_PADDING * 2;\n bottom += valueAxisHeight;\n }\n } else {\n if (valueLabels.length > 0) {\n for (const label of valueLabels) {\n const w = measure(label);\n if (w > valueAxisWidth) {\n valueAxisWidth = w;\n }\n }\n valueAxisWidth += AXIS_LABEL_PADDING * 2;\n left += valueAxisWidth;\n }\n if (categoryLabels.length > 0) {\n categoryAxisHeight = labelFontSize + AXIS_LABEL_PADDING * 2;\n bottom += categoryAxisHeight;\n }\n }\n\n const plotWidth = Math.max(0, input.width - left - right);\n const plotHeight = Math.max(0, input.height - top - bottom);\n\n const plot: Box = { x: left, y: top, width: plotWidth, height: plotHeight };\n\n const valueAxisBox: Box = input.horizontal\n ? { x: plot.x, y: plot.y + plot.height, width: plotWidth, height: valueAxisHeight }\n : { x: plot.x - valueAxisWidth, y: top, width: valueAxisWidth, height: plotHeight };\n\n const categoryAxisBox: Box = input.horizontal\n ? { x: plot.x - categoryAxisWidth, y: top, width: categoryAxisWidth, height: plotHeight }\n : { x: left, y: plot.y + plotHeight, width: plotWidth, height: categoryAxisHeight };\n\n return {\n plot,\n valueAxisBox,\n categoryAxisBox,\n titleBox,\n legendBox,\n padding,\n };\n}\n",
|
|
9
|
+
"/**\n * 文字辅助:统一的 font 字符串拼接与文本测量。\n * 所有测量都走 ICanvas2D.measureText,保证跨端一致。\n */\n\nimport type { ICanvas2D } from './canvas';\nimport { DEFAULT_FONT_FAMILY } from '../types';\n\nexport interface TextStyle {\n fontSize?: number;\n family?: string;\n}\n\n/** 构造 canvas font 字符串(小程序与 Web 语义一致)。 */\nexport function toFont(size: number, family = DEFAULT_FONT_FAMILY): string {\n return `${size}px ${family}`;\n}\n\n/** 设置当前文字样式并返回 font 字符串。 */\nexport function applyFont(\n ctx: ICanvas2D,\n size: number,\n family = DEFAULT_FONT_FAMILY\n): string {\n const font = toFont(size, family);\n ctx.font = font;\n return font;\n}\n\n/** 测量文本宽度(自动设置 font)。 */\nexport function measureWidth(\n ctx: ICanvas2D,\n text: string,\n fontSize: number,\n family = DEFAULT_FONT_FAMILY\n): number {\n applyFont(ctx, fontSize, family);\n return ctx.measureText(text).width;\n}\n\n/**\n * 截断文本到指定像素宽度(超宽加省略号)。\n * 用于类目轴标签过长时避免溢出绘图区。\n */\nexport function truncate(\n ctx: ICanvas2D,\n text: string,\n maxWidth: number,\n fontSize: number,\n family = DEFAULT_FONT_FAMILY\n): string {\n if (maxWidth <= 0 || measureWidth(ctx, text, fontSize, family) <= maxWidth) {\n return text;\n }\n const ellipsis = '…';\n let lo = 0;\n let hi = text.length;\n let result = text;\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n const candidate = text.slice(0, mid) + ellipsis;\n if (measureWidth(ctx, candidate, fontSize, family) <= maxWidth) {\n result = candidate;\n lo = mid;\n } else {\n hi = mid - 1;\n }\n }\n return result;\n}\n",
|
|
10
|
+
"/**\n * 坐标轴绘制:数值轴(默认纵向居左、横向图居底)与类目轴(默认横向居底、\n * 横向图居左)。网格线与刻度标签统一在这里绘制,数据系列只画在 plot 内。\n *\n * 位置计算全部基于布局结果与比例尺,与平台无关。\n */\n\nimport type { ICanvas2D } from './canvas';\nimport type { Box } from './layout';\nimport { CategoryScale, LinearScale } from './scale';\nimport { applyFont } from './text';\nimport {\n DEFAULT_AXIS_COLOR,\n DEFAULT_GRID_COLOR,\n DEFAULT_TEXT_COLOR,\n} from '../types';\n\nexport interface ValueAxisDrawOptions {\n /** 绘图区。 */\n plot: Box;\n /** 数值比例尺:纵向图 range=[plot.bottom, plot.top],横向图 range=[plot.left, plot.right]。 */\n scale: LinearScale;\n /** 刻度标签(长度需与 scale.ticks 一致)。 */\n labels: readonly string[];\n /** 横向图:数值轴位于底部。 */\n horizontal?: boolean;\n axisColor?: string;\n labelFontSize?: number;\n labelColor?: string;\n gridColor?: string;\n gridLineWidth?: number;\n gridLineDash?: readonly number[];\n showGrid?: boolean;\n}\n\nexport interface CategoryAxisDrawOptions {\n plot: Box;\n scale: CategoryScale;\n labels: readonly string[];\n /** 横向图:类目轴位于左侧。 */\n horizontal?: boolean;\n axisColor?: string;\n labelFontSize?: number;\n labelColor?: string;\n gridColor?: string;\n gridLineWidth?: number;\n showGrid?: boolean;\n}\n\nexport class AxisDrawer {\n public constructor(private readonly ctx: ICanvas2D) {}\n\n /** 绘制数值轴:网格横线(或纵向图时) + 刻度标签。 */\n public drawValueAxis(options: ValueAxisDrawOptions): void {\n const {\n plot,\n scale,\n labels,\n horizontal = false,\n labelFontSize = 11,\n labelColor = DEFAULT_TEXT_COLOR,\n gridColor = DEFAULT_GRID_COLOR,\n gridLineWidth = 1,\n gridLineDash,\n showGrid = true,\n } = options;\n\n const ticks = scale.ticks;\n const { ctx } = this;\n\n for (let i = 0; i < ticks.length; i += 1) {\n const pos = scale.scale(ticks[i]);\n const label = labels[i] ?? String(ticks[i]);\n\n if (showGrid) {\n ctx.save();\n ctx.strokeStyle = gridColor;\n ctx.lineWidth = gridLineWidth;\n if (gridLineDash && gridLineDash.length > 0) {\n ctx.setLineDash(gridLineDash);\n }\n ctx.beginPath();\n if (horizontal) {\n ctx.moveTo(plot.x, pos);\n ctx.lineTo(plot.x + plot.width, pos);\n } else {\n ctx.moveTo(plot.x, pos);\n ctx.lineTo(plot.x + plot.width, pos);\n }\n ctx.stroke();\n ctx.restore();\n }\n\n applyFont(ctx, labelFontSize);\n ctx.fillStyle = labelColor;\n ctx.textBaseline = 'middle';\n if (horizontal) {\n ctx.textAlign = 'center';\n ctx.fillText(label, pos, plot.y + plot.height + 8);\n } else {\n ctx.textAlign = 'right';\n ctx.fillText(label, plot.x - 8, pos);\n }\n }\n }\n\n /** 绘制类目轴:轴线 + 类目标签(可选竖网格)。 */\n public drawCategoryAxis(options: CategoryAxisDrawOptions): void {\n const {\n plot,\n scale,\n labels,\n horizontal = false,\n axisColor = DEFAULT_AXIS_COLOR,\n labelFontSize = 11,\n labelColor = DEFAULT_TEXT_COLOR,\n gridColor = DEFAULT_GRID_COLOR,\n gridLineWidth = 1,\n showGrid = false,\n } = options;\n\n const { ctx } = this;\n const count = labels.length;\n\n if (showGrid) {\n ctx.save();\n ctx.strokeStyle = gridColor;\n ctx.lineWidth = gridLineWidth;\n ctx.beginPath();\n for (let i = 0; i < count; i += 1) {\n const pos = scale.center(i);\n if (horizontal) {\n ctx.moveTo(plot.x, pos);\n ctx.lineTo(plot.x + plot.width, pos);\n } else {\n ctx.moveTo(pos, plot.y);\n ctx.lineTo(pos, plot.y + plot.height);\n }\n }\n ctx.stroke();\n ctx.restore();\n }\n\n applyFont(ctx, labelFontSize);\n ctx.fillStyle = labelColor;\n ctx.textBaseline = 'middle';\n\n for (let i = 0; i < count; i += 1) {\n const pos = scale.center(i);\n if (horizontal) {\n ctx.textAlign = 'right';\n ctx.fillText(labels[i], plot.x - 8, pos);\n } else {\n ctx.textAlign = 'center';\n ctx.fillText(labels[i], pos, plot.y + plot.height + 8);\n }\n }\n\n // 轴线\n ctx.save();\n ctx.strokeStyle = axisColor;\n ctx.lineWidth = 1;\n ctx.beginPath();\n if (horizontal) {\n ctx.moveTo(plot.x, plot.y);\n ctx.lineTo(plot.x, plot.y + plot.height);\n } else {\n ctx.moveTo(plot.x, plot.y + plot.height);\n ctx.lineTo(plot.x + plot.width, plot.y + plot.height);\n }\n ctx.stroke();\n ctx.restore();\n }\n}\n",
|
|
11
|
+
"/**\n * 图例绘制:色块 + 文本。\n * - top / bottom:横向单行居中排布\n * - right:纵向排布\n */\n\nimport type { ICanvas2D } from './canvas';\nimport type { Box } from './layout';\nimport { applyFont, measureWidth } from './text';\nimport { DEFAULT_TEXT_COLOR } from '../types';\n\nexport interface LegendItem {\n name: string;\n color: string;\n}\n\nexport interface LegendDrawOptions {\n box: Box;\n items: readonly LegendItem[];\n position?: 'top' | 'bottom' | 'right';\n fontSize?: number;\n color?: string;\n markerSize?: number;\n itemGap?: number;\n}\n\nexport class LegendDrawer {\n public constructor(private readonly ctx: ICanvas2D) {}\n\n public draw(options: LegendDrawOptions): void {\n const {\n box,\n items,\n position = 'top',\n fontSize = 12,\n color = DEFAULT_TEXT_COLOR,\n markerSize = 12,\n itemGap = 16,\n } = options;\n\n if (items.length === 0) {\n return;\n }\n\n const { ctx } = this;\n applyFont(ctx, fontSize);\n\n if (position === 'right') {\n // 纵向:从顶部开始逐项排列\n let y = box.y + fontSize / 2 + 4;\n for (const item of items) {\n this.drawItem(item, box.x, y, fontSize, markerSize, color);\n y += fontSize + 8;\n }\n return;\n }\n\n // 横向:居中排布\n let totalWidth = 0;\n for (const item of items) {\n totalWidth += markerSize + 6 + measureWidth(ctx, item.name, fontSize) + itemGap;\n }\n totalWidth -= itemGap;\n\n let x = box.x + (box.width - totalWidth) / 2;\n const y = box.y + box.height / 2;\n\n for (const item of items) {\n this.drawItem(item, x, y, fontSize, markerSize, color);\n x += markerSize + 6 + measureWidth(ctx, item.name, fontSize) + itemGap;\n }\n }\n\n private drawItem(\n item: LegendItem,\n x: number,\n centerY: number,\n _fontSize: number,\n markerSize: number,\n color: string\n ): void {\n const { ctx } = this;\n // 色块\n ctx.fillStyle = item.color;\n ctx.beginPath();\n ctx.rect(x, centerY - markerSize / 2, markerSize, markerSize);\n ctx.fill();\n // 文本\n ctx.fillStyle = color;\n ctx.textAlign = 'left';\n ctx.textBaseline = 'middle';\n ctx.fillText(item.name, x + markerSize + 6, centerY);\n }\n}\n",
|
|
12
|
+
"/**\n * ChartBase:所有图表的抽象基类,负责统一渲染管线。\n *\n * 渲染管线(render):\n * save → scale(dpr) → clear → 背景 → 布局计算\n * → 标题 → 图例 → 笛卡尔坐标轴(折线/柱状)\n * → drawSeries(子类策略) → restore\n *\n * 生命周期:setOption 增量更新 → render;resize 改逻辑尺寸 → render;\n * destroy 释放。所有坐标均为逻辑像素(CSS px),DPR 由管线统一缩放。\n */\n\nimport type { ICanvas2D } from './canvas';\nimport { AxisDrawer } from './axis';\nimport {\n computeLayout,\n type LayoutInput,\n type LayoutResult,\n} from './layout';\nimport { LegendDrawer, type LegendItem } from './legend';\nimport { CategoryScale, LinearScale } from './scale';\nimport { applyFont, measureWidth } from './text';\nimport {\n DEFAULT_PALETTE,\n type CategoryAxisOption,\n type ChartOption,\n type ChartRenderContext,\n type LegendOption,\n type TitleOption,\n type ValueAxisOption,\n} from '../types';\n\nexport interface CartesianScales {\n value: LinearScale;\n category: CategoryScale;\n}\n\n/**\n * 子类需要实现的钩子:\n * - drawSeries:绘制数据系列(核心策略)\n * - hasCartesianAxis:是否需要坐标轴(饼图/雷达返回 false)\n * - getValueDomain:数值轴数据域(有轴时)\n * - getCategoryLabels:类目轴标签(有轴时)\n * - isHorizontal:横向布局(横向柱状图)\n */\nexport abstract class ChartBase<T extends ChartOption> {\n protected readonly ctx: ICanvas2D;\n protected option: T;\n protected width: number;\n protected height: number;\n protected dpr: number;\n protected readonly palette: readonly string[];\n\n /** 每次 render 构建的笛卡尔比例尺,供 drawSeries 读取。 */\n protected cartesian: CartesianScales | null = null;\n\n private destroyed = false;\n\n public constructor(context: ChartRenderContext, option: T) {\n this.ctx = context.ctx;\n this.option = option;\n this.width = context.width;\n this.height = context.height;\n this.dpr = context.dpr;\n this.palette = context.palette ?? DEFAULT_PALETTE;\n }\n\n /* —— 生命周期 —— */\n\n public setOption(option: Partial<T>): this {\n this.option = { ...this.option, ...option };\n return this;\n }\n\n public resize(width: number, height: number): this {\n this.width = Math.max(0, width);\n this.height = Math.max(0, height);\n return this;\n }\n\n public render(): this {\n if (this.destroyed) {\n return this;\n }\n const { ctx, width, height, dpr } = this;\n\n ctx.save();\n ctx.scale(dpr, dpr);\n ctx.clearRect(0, 0, width, height);\n\n const background = this.getBackgroundColor();\n if (background) {\n ctx.fillStyle = background;\n ctx.fillRect(0, 0, width, height);\n }\n\n const layout = this.computeLayout();\n this.drawTitle(layout);\n this.drawLegend(layout);\n\n if (this.hasCartesianAxis()) {\n this.cartesian = this.buildCartesianScales(layout);\n this.drawCartesianAxis(layout);\n } else {\n this.cartesian = null;\n }\n\n this.drawSeries(ctx, layout, this.option);\n\n ctx.restore();\n return this;\n }\n\n public destroy(): void {\n this.destroyed = true;\n }\n\n public isDestroyed(): boolean {\n return this.destroyed;\n }\n\n /* —— 子类策略钩子 —— */\n\n protected abstract drawSeries(\n ctx: ICanvas2D,\n layout: LayoutResult,\n option: T\n ): void;\n\n protected hasCartesianAxis(): boolean {\n return true;\n }\n\n protected getValueDomain(): { min: number; max: number } {\n return { min: 0, max: 1 };\n }\n\n protected getCategoryLabels(): readonly string[] {\n return [];\n }\n\n protected isHorizontal(): boolean {\n return false;\n }\n\n /* —— 可覆盖的配置来源 —— */\n\n protected getBackgroundColor(): string | undefined {\n return (this.option as { backgroundColor?: string }).backgroundColor;\n }\n\n protected getTitle(): TitleOption | undefined {\n return (this.option as { title?: TitleOption }).title;\n }\n\n protected getLegend(): LegendOption | undefined {\n const legend = (this.option as { legend?: LegendOption }).legend;\n if (legend) {\n return legend;\n }\n // 未显式配置图例但存在命名系列/扇区时,默认在顶部显示\n return this.getLegendItems().length > 0 ? { position: 'top' } : undefined;\n }\n\n protected getLegendItems(): LegendItem[] {\n return [];\n }\n\n protected getValueAxis(): ValueAxisOption | undefined {\n return (this.option as { yAxis?: ValueAxisOption }).yAxis;\n }\n\n protected getCategoryAxis(): CategoryAxisOption | undefined {\n return (this.option as { xAxis?: CategoryAxisOption }).xAxis;\n }\n\n /* —— 渲染管线内部 —— */\n\n protected computeLayout(): LayoutResult {\n const input = this.buildLayoutInput();\n const measure = (text: string): number =>\n measureWidth(this.ctx, text, 11);\n return computeLayout(input, measure);\n }\n\n protected buildLayoutInput(): LayoutInput {\n // 轴标签区需要知道刻度标签宽度,而刻度与布局无关(仅依赖数据域),\n // 因此用占位 range 预计算刻度 → 布局 → 最终 scale 两阶段完成。\n const horizontal = this.isHorizontal();\n const hasAxis = this.hasCartesianAxis();\n return {\n width: this.width,\n height: this.height,\n title: this.getTitle(),\n legend: this.getLegend(),\n legendItems: this.getLegendItems().map((item) => item.name),\n valueLabels: hasAxis && !horizontal ? this.computeValueLabels() : undefined,\n categoryLabels: hasAxis ? this.getCategoryLabels() : undefined,\n horizontal,\n };\n }\n\n /** 用占位 range 预计算数值刻度标签(仅用于测量,不参与最终映射)。 */\n protected computeValueLabels(): string[] {\n const domain = this.getValueDomain();\n const valueAxis = this.getValueAxis();\n const probe = new LinearScale(domain.min, domain.max, 0, 1, {\n min: valueAxis?.min,\n max: valueAxis?.max,\n splitCount: valueAxis?.splitCount,\n });\n return probe.ticks.map((tick) =>\n valueAxis?.format ? valueAxis.format(tick) : String(tick)\n );\n }\n\n protected drawTitle(layout: LayoutResult): void {\n const title = this.getTitle();\n if (!title?.text || !layout.titleBox) {\n return;\n }\n const { ctx } = this;\n applyFont(ctx, title.fontSize ?? 16);\n ctx.fillStyle = title.color ?? '#323233';\n ctx.textAlign = 'center';\n ctx.textBaseline = 'top';\n ctx.fillText(title.text, this.width / 2, layout.titleBox.y);\n }\n\n protected drawLegend(layout: LayoutResult): void {\n const legend = this.getLegend();\n if (legend?.show === false || !layout.legendBox) {\n return;\n }\n const items = this.getLegendItems();\n if (items.length === 0) {\n return;\n }\n new LegendDrawer(this.ctx).draw({\n box: layout.legendBox,\n items,\n position: legend?.position ?? 'top',\n fontSize: legend?.fontSize,\n color: legend?.color,\n markerSize: legend?.markerSize,\n itemGap: legend?.itemGap,\n });\n }\n\n protected buildCartesianScales(layout: LayoutResult): CartesianScales {\n const { plot } = layout;\n const horizontal = this.isHorizontal();\n const domain = this.getValueDomain();\n const categories = this.getCategoryLabels();\n const valueAxis = this.getValueAxis();\n\n const value = new LinearScale(\n domain.min,\n domain.max,\n horizontal ? plot.x : plot.y + plot.height,\n horizontal ? plot.x + plot.width : plot.y,\n { min: valueAxis?.min, max: valueAxis?.max, splitCount: valueAxis?.splitCount }\n );\n\n const category = new CategoryScale(\n categories,\n horizontal ? plot.y : plot.x,\n horizontal ? plot.y + plot.height : plot.x + plot.width\n );\n\n return { value, category };\n }\n\n protected drawCartesianAxis(layout: LayoutResult): void {\n const scales = this.cartesian;\n if (!scales) {\n return;\n }\n const { ctx } = this;\n const { plot } = layout;\n const horizontal = this.isHorizontal();\n const valueAxis = this.getValueAxis();\n const categoryAxis = this.getCategoryAxis();\n const categories = this.getCategoryLabels();\n\n const drawer = new AxisDrawer(ctx);\n\n const valueLabels = scales.value.ticks.map((tick) =>\n valueAxis?.format ? valueAxis.format(tick) : String(tick)\n );\n\n drawer.drawValueAxis({\n plot,\n scale: scales.value,\n labels: valueLabels,\n horizontal,\n labelFontSize: valueAxis?.labelFontSize,\n labelColor: valueAxis?.labelColor,\n gridColor: valueAxis?.gridColor,\n gridLineWidth: valueAxis?.gridLineWidth,\n gridLineDash: valueAxis?.gridLineDash,\n showGrid: valueAxis?.showGrid ?? true,\n });\n\n drawer.drawCategoryAxis({\n plot,\n scale: scales.category,\n labels: categories,\n horizontal,\n labelFontSize: categoryAxis?.labelFontSize,\n labelColor: categoryAxis?.labelColor,\n gridColor: categoryAxis?.gridColor,\n gridLineWidth: categoryAxis?.gridLineWidth,\n showGrid: categoryAxis?.showGrid ?? false,\n });\n }\n\n /** 系列取色:显式 color 优先,否则按索引取色板。 */\n protected seriesColor(index: number, color?: string): string {\n return color ?? this.palette[index % this.palette.length];\n }\n}\n",
|
|
13
|
+
"/**\n * 折线图:多系列 / 平滑曲线 / 面积填充 / 数据点标记。\n * 类目 x 轴 + 数值 y 轴,复用 ChartBase 的笛卡尔管线。\n */\n\nimport type { ICanvas2D } from '../core/canvas';\nimport { ChartBase } from '../core/chart';\nimport type { LayoutResult } from '../core/layout';\nimport type { LegendItem } from '../core/legend';\nimport type { LineChartOption } from '../types';\n\ninterface Point {\n x: number;\n y: number;\n}\n\nexport class LineChart extends ChartBase<LineChartOption> {\n protected getValueDomain(): { min: number; max: number } {\n const { series, yAxis, startFromZero = false } = this.option;\n let min = Infinity;\n let max = -Infinity;\n for (const s of series) {\n for (const value of s.data) {\n if (value < min) {\n min = value;\n }\n if (value > max) {\n max = value;\n }\n }\n }\n if (!Number.isFinite(min) || !Number.isFinite(max)) {\n return { min: 0, max: 1 };\n }\n if (startFromZero && min > 0) {\n min = 0;\n }\n if (yAxis?.min !== undefined) {\n min = Math.min(min, yAxis.min);\n }\n if (yAxis?.max !== undefined) {\n max = Math.max(max, yAxis.max);\n }\n return { min, max };\n }\n\n protected getCategoryLabels(): readonly string[] {\n return this.option.xAxis.labels;\n }\n\n protected getLegendItems(): LegendItem[] {\n return this.option.series\n .filter((s) => s.name)\n .map((s, i) => ({\n name: s.name as string,\n color: this.seriesColor(i, s.color),\n }));\n }\n\n protected drawSeries(\n ctx: ICanvas2D,\n layout: LayoutResult,\n option: LineChartOption\n ): void {\n const scales = this.cartesian;\n if (!scales) {\n return;\n }\n const { plot } = layout;\n const { value, category } = scales;\n\n const pointsPerSeries: Point[][] = option.series.map((series) =>\n series.data.map((d, i) => ({\n x: category.center(i),\n y: value.scale(d),\n }))\n );\n\n option.series.forEach((series, index) => {\n const points = pointsPerSeries[index];\n if (points.length === 0) {\n return;\n }\n const color = this.seriesColor(index, series.color);\n const lineWidth = series.lineWidth ?? 2;\n\n // 面积填充(先画,保证在折线之下)\n if (series.area) {\n ctx.save();\n ctx.fillStyle = color;\n ctx.globalAlpha = 0.15;\n ctx.beginPath();\n this.tracePath(ctx, points, series.smooth ?? false);\n ctx.lineTo(points[points.length - 1].x, plot.y + plot.height);\n ctx.lineTo(points[0].x, plot.y + plot.height);\n ctx.closePath();\n ctx.fill();\n ctx.restore();\n }\n\n // 折线\n ctx.save();\n ctx.strokeStyle = color;\n ctx.lineWidth = lineWidth;\n ctx.lineJoin = 'round';\n ctx.lineCap = 'round';\n ctx.beginPath();\n this.tracePath(ctx, points, series.smooth ?? false);\n ctx.stroke();\n ctx.restore();\n\n // 数据点标记\n if (series.showSymbol !== false) {\n ctx.save();\n ctx.fillStyle = color;\n for (const p of points) {\n ctx.beginPath();\n ctx.arc(p.x, p.y, Math.max(2.5, lineWidth + 0.5), 0, Math.PI * 2);\n ctx.fill();\n }\n ctx.restore();\n }\n });\n }\n\n /** 折线路径:直线或 Catmull-Rom 平滑插值(三次贝塞尔)。 */\n private tracePath(ctx: ICanvas2D, points: readonly Point[], smooth: boolean): void {\n if (points.length === 0) {\n return;\n }\n ctx.moveTo(points[0].x, points[0].y);\n if (!smooth || points.length < 3) {\n for (let i = 1; i < points.length; i += 1) {\n ctx.lineTo(points[i].x, points[i].y);\n }\n return;\n }\n\n for (let i = 0; i < points.length - 1; i += 1) {\n const p0 = points[Math.max(0, i - 1)];\n const p1 = points[i];\n const p2 = points[i + 1];\n const p3 = points[Math.min(points.length - 1, i + 2)];\n\n const cp1x = p1.x + (p2.x - p0.x) / 6;\n const cp1y = p1.y + (p2.y - p0.y) / 6;\n const cp2x = p2.x - (p3.x - p1.x) / 6;\n const cp2y = p2.y - (p3.y - p1.y) / 6;\n\n ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, p2.x, p2.y);\n }\n }\n}\n",
|
|
14
|
+
"/**\n * 柱状图:纵向 / 横向,支持多系列分组与 stack 堆叠。\n *\n * 几何规则:\n * - 分组:同一类目内多个系列并排,子柱宽 = 类目带宽 × (1 - gap) / 系列数 × 0.8\n * - 堆叠:同名 stack 系列共用一根柱,后一个系列从前一个的顶端继续累加\n * - 数值轴强制包含 0(柱形高度必须从零线起算才有意义)\n */\n\nimport type { ICanvas2D } from '../core/canvas';\nimport { ChartBase } from '../core/chart';\nimport type { LayoutResult } from '../core/layout';\nimport type { LegendItem } from '../core/legend';\nimport type { BarChartOption, BarSeries } from '../types';\n\n/** 柱体在类目带内的水平偏移与宽度(纵向)或垂直偏移与高度(横向)。 */\ninterface Slot {\n offset: number;\n size: number;\n}\n\nexport class BarChart extends ChartBase<BarChartOption> {\n protected isHorizontal(): boolean {\n return this.option.horizontal ?? false;\n }\n\n protected getValueDomain(): { min: number; max: number } {\n const { series, yAxis } = this.option;\n let max = 0;\n let min = 0;\n\n // 堆叠:按 stack 组在类目上累加正负\n const stackGroups = groupStacks(series);\n const categoryCount = Math.max(\n 0,\n ...series.map((s) => s.data.length)\n );\n\n for (let i = 0; i < categoryCount; i += 1) {\n for (const group of stackGroups) {\n let positive = 0;\n let negative = 0;\n for (const s of group) {\n const v = s.data[i] ?? 0;\n if (v >= 0) {\n positive += v;\n } else {\n negative += v;\n }\n }\n if (positive > max) {\n max = positive;\n }\n if (negative < min) {\n min = negative;\n }\n }\n }\n\n if (yAxis?.min !== undefined) {\n min = Math.min(min, yAxis.min);\n }\n if (yAxis?.max !== undefined) {\n max = Math.max(max, yAxis.max);\n }\n return { min, max };\n }\n\n protected getCategoryLabels(): readonly string[] {\n return this.option.xAxis.labels;\n }\n\n protected getLegendItems(): LegendItem[] {\n return this.option.series\n .filter((s) => s.name)\n .map((s, i) => ({\n name: s.name as string,\n color: this.seriesColor(i, s.color),\n }));\n }\n\n protected drawSeries(\n ctx: ICanvas2D,\n layout: LayoutResult,\n option: BarChartOption\n ): void {\n const scales = this.cartesian;\n if (!scales) {\n return;\n }\n void layout; // 柱体几何全部由比例尺绝对坐标决定,无需 plot 引用\n const { value, category } = scales;\n const horizontal = option.horizontal ?? false;\n\n const stackGroups = groupStacks(option.series);\n const categoryCount = option.xAxis.labels.length;\n\n for (let i = 0; i < categoryCount; i += 1) {\n // 每个类目带内:无堆叠时按系列分组并排;有堆叠时按 stack 组并排\n const groups = stackGroups;\n const seriesCount = groups.length;\n const explicitWidth = option.series\n .map((s) => s.barWidth)\n .find((w) => w !== undefined && w > 0);\n const slot = this.computeSlot(category, seriesCount, explicitWidth);\n\n groups.forEach((group, groupIndex) => {\n const slotOffset = slot.offset + groupIndex * slot.size;\n let cursor = 0; // 堆叠游标(绝对值累加)\n\n group.forEach((series) => {\n const raw = series.data[i] ?? 0;\n const color = this.seriesColor(\n option.series.indexOf(series),\n series.color\n );\n\n if (horizontal) {\n const x0 = value.scale(0);\n const x1 = value.scale(cursor + raw);\n const base = Math.min(x0, x1);\n const width = Math.abs(x1 - x0);\n const y = category.bandStart(i) + slotOffset;\n this.drawBar(ctx, {\n x: base,\n y,\n width,\n height: slot.size,\n radius: series.borderRadius ?? 0,\n color,\n roundSide: raw >= 0 ? 'right' : 'left',\n });\n } else {\n const y0 = value.scale(cursor);\n const y1 = value.scale(cursor + raw);\n const top = Math.min(y0, y1);\n const height = Math.abs(y1 - y0);\n const x = category.bandStart(i) + slotOffset;\n this.drawBar(ctx, {\n x,\n y: top,\n width: slot.size,\n height,\n radius: series.borderRadius ?? 0,\n color,\n roundSide: raw >= 0 ? 'top' : 'bottom',\n });\n }\n\n cursor += raw;\n });\n });\n }\n }\n\n private computeSlot(\n category: { bandStart(index: number): number; innerWidth(): number },\n groupCount: number,\n explicitWidth?: number\n ): Slot {\n if (explicitWidth !== undefined) {\n // 显式柱宽:组内居中\n return {\n offset: (category.innerWidth() - explicitWidth) / 2,\n size: explicitWidth,\n };\n }\n const size = category.innerWidth() / Math.max(1, groupCount) * 0.8;\n const gap = (category.innerWidth() - size * groupCount) / 2;\n return { offset: gap, size };\n }\n\n private drawBar(\n ctx: ICanvas2D,\n bar: {\n x: number;\n y: number;\n width: number;\n height: number;\n radius: number;\n color: string;\n roundSide: 'top' | 'bottom' | 'right' | 'left';\n }\n ): void {\n if (bar.width <= 0 || bar.height <= 0) {\n return;\n }\n ctx.save();\n ctx.fillStyle = bar.color;\n ctx.beginPath();\n roundRectPath(ctx, bar, bar.radius, bar.roundSide);\n ctx.fill();\n ctx.restore();\n }\n}\n\n/** 按 stack 分组:无 stack 的系列各自成组;同名 stack 合并为一组。 */\nfunction groupStacks(series: readonly BarSeries[]): BarSeries[][] {\n const groups: BarSeries[][] = [];\n const stackMap = new Map<string, BarSeries[]>();\n\n for (const s of series) {\n if (s.stack) {\n let group = stackMap.get(s.stack);\n if (!group) {\n group = [];\n stackMap.set(s.stack, group);\n groups.push(group);\n }\n group.push(s);\n } else {\n groups.push([s]);\n }\n }\n return groups;\n}\n\n/**\n * 圆角柱体路径:仅 roundSide 指示的一侧(两个角)圆角。\n * - 纵向柱:'top' 顶部两角 / 'bottom' 底部两角\n * - 横向柱:'right' 右侧两角 / 'left' 左侧两角\n */\nfunction roundRectPath(\n ctx: ICanvas2D,\n bar: { x: number; y: number; width: number; height: number },\n radius: number,\n roundSide: 'top' | 'bottom' | 'right' | 'left'\n): void {\n const { x, y, width, height } = bar;\n const r = Math.min(radius, width / 2, height / 2);\n if (r <= 0) {\n ctx.rect(x, y, width, height);\n return;\n }\n\n const roundTopLeft = roundSide === 'top' || roundSide === 'left';\n const roundTopRight = roundSide === 'top' || roundSide === 'right';\n const roundBottomLeft = roundSide === 'bottom' || roundSide === 'left';\n const roundBottomRight = roundSide === 'bottom' || roundSide === 'right';\n\n ctx.moveTo(x + (roundTopLeft ? r : 0), y);\n ctx.lineTo(x + width - (roundTopRight ? r : 0), y);\n if (roundTopRight) {\n ctx.arcTo(x + width, y, x + width, y + r, r);\n } else {\n ctx.lineTo(x + width, y);\n }\n\n ctx.lineTo(x + width, y + height - (roundBottomRight ? r : 0));\n if (roundBottomRight) {\n ctx.arcTo(x + width, y + height, x + width - r, y + height, r);\n } else {\n ctx.lineTo(x + width, y + height);\n }\n\n ctx.lineTo(x + (roundBottomLeft ? r : 0), y + height);\n if (roundBottomLeft) {\n ctx.arcTo(x, y + height, x, y + height - r, r);\n } else {\n ctx.lineTo(x, y + height);\n }\n\n ctx.lineTo(x, y + (roundTopLeft ? r : 0));\n if (roundTopLeft) {\n ctx.arcTo(x, y, x + r, y, r);\n } else {\n ctx.lineTo(x, y);\n }\n ctx.closePath();\n}\n",
|
|
15
|
+
"/**\n * 饼图 / 环形图:扇区绘制、百分比标签、图例。\n * 无笛卡尔坐标轴,全部几何在绘图区内按极坐标计算。\n */\n\nimport type { ICanvas2D } from '../core/canvas';\nimport { ChartBase } from '../core/chart';\nimport type { LayoutResult } from '../core/layout';\nimport type { LegendItem } from '../core/legend';\nimport { applyFont } from '../core/text';\nimport type { PieChartOption } from '../types';\n\nconst TWO_PI = Math.PI * 2;\n\nexport class PieChart extends ChartBase<PieChartOption> {\n protected hasCartesianAxis(): boolean {\n return false;\n }\n\n protected getLegendItems(): LegendItem[] {\n return this.option.data.map((d, i) => ({\n name: d.name,\n color: d.color ?? this.seriesColor(i),\n }));\n }\n\n protected drawSeries(\n ctx: ICanvas2D,\n layout: LayoutResult,\n option: PieChartOption\n ): void {\n const { plot } = layout;\n const centerX = plot.x + plot.width / 2;\n const centerY = plot.y + plot.height / 2;\n\n const maxRadius =\n Math.min(plot.width, plot.height) / 2;\n const outerRadius = resolveRadius(option.radius ?? '60%', maxRadius);\n const innerRadius = resolveRadius(option.innerRadius ?? 0, maxRadius);\n const startAngle = option.startAngle ?? -Math.PI / 2;\n\n const data = option.data.filter((d) => d.value > 0);\n const total = data.reduce((sum, d) => sum + d.value, 0);\n if (total <= 0 || outerRadius <= 0) {\n return;\n }\n\n let angle = startAngle;\n\n data.forEach((d, index) => {\n const sweep = (d.value / total) * TWO_PI;\n const color = d.color ?? this.seriesColor(index);\n\n ctx.save();\n ctx.fillStyle = color;\n ctx.beginPath();\n if (innerRadius > 0) {\n // 环形:外弧 → 内弧(反向)闭合\n ctx.arc(centerX, centerY, outerRadius, angle, angle + sweep);\n ctx.arc(\n centerX,\n centerY,\n innerRadius,\n angle + sweep,\n angle,\n true\n );\n } else {\n ctx.arc(centerX, centerY, outerRadius, angle, angle + sweep);\n ctx.lineTo(centerX, centerY);\n }\n ctx.closePath();\n ctx.fill();\n // 扇区间分隔线\n ctx.strokeStyle = '#ffffff';\n ctx.lineWidth = 1;\n ctx.stroke();\n ctx.restore();\n\n // 扇区标签:名称 + 百分比,画在扇区角平分线上\n if (option.showLabel !== false) {\n const midAngle = angle + sweep / 2;\n const labelRadius = (outerRadius + (innerRadius > 0 ? innerRadius : 0)) / 2 * 0.85;\n const labelX = centerX + Math.cos(midAngle) * labelRadius;\n const labelY = centerY + Math.sin(midAngle) * labelRadius;\n const percent = total > 0 ? Math.round((d.value / total) * 100) : 0;\n\n applyFont(ctx, option.labelFontSize ?? 10);\n ctx.fillStyle = option.labelColor ?? '#ffffff';\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n ctx.fillText(`${d.name} ${percent}%`, labelX, labelY);\n }\n\n angle += sweep;\n });\n }\n}\n\n/** 解析半径:数字为像素;'60%' 形式按最大半径百分比。 */\nfunction resolveRadius(value: number | string, maxRadius: number): number {\n if (typeof value === 'number') {\n return Math.max(0, value);\n }\n const match = /^(\\d+(?:\\.\\d+)?)%$/.exec(value);\n if (!match) {\n return maxRadius;\n }\n return (Number(match[1]) / 100) * maxRadius;\n}\n",
|
|
16
|
+
"/**\n * 雷达图:多边形网格 + 指标轴 + 多系列区域/折线。\n * 指标刻度按各 indicator.max(缺省取所有系列该指标最大值)归一化。\n */\n\nimport type { ICanvas2D } from '../core/canvas';\nimport { ChartBase } from '../core/chart';\nimport type { LayoutResult } from '../core/layout';\nimport type { LegendItem } from '../core/legend';\nimport { applyFont } from '../core/text';\nimport type { RadarChartOption } from '../types';\n\ninterface PolarPoint {\n x: number;\n y: number;\n}\n\nexport class RadarChart extends ChartBase<RadarChartOption> {\n protected hasCartesianAxis(): boolean {\n return false;\n }\n\n protected getLegendItems(): LegendItem[] {\n return this.option.series\n .filter((s) => s.name)\n .map((s, i) => ({\n name: s.name as string,\n color: this.seriesColor(i, s.color),\n }));\n }\n\n protected drawSeries(\n ctx: ICanvas2D,\n layout: LayoutResult,\n option: RadarChartOption\n ): void {\n const { plot } = layout;\n const centerX = plot.x + plot.width / 2;\n const centerY = plot.y + plot.height / 2;\n const radius =\n option.radius ?? Math.min(plot.width, plot.height) / 2 * 0.6;\n const count = option.indicators.length;\n if (count === 0 || radius <= 0) {\n return;\n }\n\n const startAngle = option.startAngle ?? -Math.PI / 2;\n const step = TWO_PI / count;\n const splitCount = Math.max(1, option.splitCount ?? 5);\n const gridColor = option.gridColor ?? '#ebedf0';\n const gridLineWidth = option.gridLineWidth ?? 1;\n\n const vertexAngle = (index: number): number =>\n startAngle + index * step;\n\n const polarPoint = (angle: number, r: number): PolarPoint => ({\n x: centerX + Math.cos(angle) * r,\n y: centerY + Math.sin(angle) * r,\n });\n\n // 网格层:同心多边形\n for (let layer = 1; layer <= splitCount; layer += 1) {\n const r = (radius * layer) / splitCount;\n ctx.save();\n ctx.strokeStyle = gridColor;\n ctx.lineWidth = gridLineWidth;\n ctx.beginPath();\n for (let i = 0; i < count; i += 1) {\n const p = polarPoint(vertexAngle(i), r);\n if (i === 0) {\n ctx.moveTo(p.x, p.y);\n } else {\n ctx.lineTo(p.x, p.y);\n }\n }\n ctx.closePath();\n ctx.stroke();\n ctx.restore();\n }\n\n // 指标轴线\n ctx.save();\n ctx.strokeStyle = gridColor;\n ctx.lineWidth = gridLineWidth;\n ctx.beginPath();\n for (let i = 0; i < count; i += 1) {\n const p = polarPoint(vertexAngle(i), radius);\n ctx.moveTo(centerX, centerY);\n ctx.lineTo(p.x, p.y);\n }\n ctx.stroke();\n ctx.restore();\n\n // 指标标签(顶点外侧)\n const labelFontSize = option.labelFontSize ?? 11;\n applyFont(ctx, labelFontSize);\n ctx.fillStyle = option.labelColor ?? '#323233';\n for (let i = 0; i < count; i += 1) {\n const p = polarPoint(vertexAngle(i), radius + 12);\n ctx.textAlign = Math.abs(p.x - centerX) < 1 ? 'center' : p.x > centerX ? 'left' : 'right';\n ctx.textBaseline =\n Math.abs(p.y - centerY) < 1\n ? 'middle'\n : p.y > centerY\n ? 'top'\n : 'bottom';\n ctx.fillText(option.indicators[i].name, p.x, p.y);\n }\n\n // 系列区域 / 折线\n option.series.forEach((series, seriesIndex) => {\n const color = this.seriesColor(seriesIndex, series.color);\n const points: PolarPoint[] = option.indicators.map((indicator, i) => {\n const max =\n indicator.max ?? this.indicatorMax(option, i);\n const value = series.data[i] ?? 0;\n const ratio = max > 0 ? Math.max(0, Math.min(1, value / max)) : 0;\n return polarPoint(vertexAngle(i), radius * ratio);\n });\n\n if (series.area !== false) {\n ctx.save();\n ctx.fillStyle = color;\n ctx.globalAlpha = 0.2;\n ctx.beginPath();\n this.tracePolygon(ctx, points);\n ctx.closePath();\n ctx.fill();\n ctx.restore();\n }\n\n ctx.save();\n ctx.strokeStyle = color;\n ctx.lineWidth = series.lineWidth ?? 2;\n ctx.lineJoin = 'round';\n ctx.beginPath();\n this.tracePolygon(ctx, points);\n ctx.closePath();\n ctx.stroke();\n ctx.restore();\n });\n }\n\n private indicatorMax(option: RadarChartOption, index: number): number {\n let max = 0;\n for (const series of option.series) {\n const v = series.data[index] ?? 0;\n if (v > max) {\n max = v;\n }\n }\n return max;\n }\n\n private tracePolygon(ctx: ICanvas2D, points: readonly PolarPoint[]): void {\n if (points.length === 0) {\n return;\n }\n ctx.moveTo(points[0].x, points[0].y);\n for (let i = 1; i < points.length; i += 1) {\n ctx.lineTo(points[i].x, points[i].y);\n }\n }\n}\n\nconst TWO_PI = Math.PI * 2;\n",
|
|
17
|
+
"/**\n * 平台适配层:把各端 Canvas 2D 上下文解析为统一渲染上下文\n * (ICanvas2D + 逻辑尺寸 + DPR)。\n *\n * 适配器只负责三件事:\n * 1. 拿到上下文(HTMLCanvasElement / 小程序 canvas node / 未来原生 canvas)\n * 2. 计算设备像素比并设置物理画布尺寸\n * 3. 返回引擎需要的 ChartRenderContext\n *\n * 引擎代码永远不直接接触 DOM / wx / native API。\n */\n\nimport type { ChartRenderContext } from '../types';\n\nexport interface ResolveCanvasOptions {\n /** 逻辑宽度(CSS px);缺省取节点实际尺寸。 */\n width?: number;\n /** 逻辑高度(CSS px);缺省取节点实际尺寸。 */\n height?: number;\n /** 设备像素比;缺省自动探测。 */\n dpr?: number;\n /** 自定义色板。 */\n palette?: readonly string[];\n}\n\nexport interface ResolvedCanvas {\n context: ChartRenderContext;\n}\n\n/** 通用探测设备像素比(web 与小程序可用 globalThis 访问)。 */\nexport function detectPixelRatio(): number {\n const host = getHost();\n if (typeof host.devicePixelRatio === 'number' && host.devicePixelRatio > 0) {\n return host.devicePixelRatio;\n }\n return 1;\n}\n\nfunction getHost(): Record<string, unknown> {\n if (typeof globalThis !== 'undefined') {\n return globalThis as unknown as Record<string, unknown>;\n }\n return {};\n}\n",
|
|
18
|
+
"/**\n * 小程序适配器:微信 / 阿里 / 字节的 Canvas 2D 节点 → ChartRenderContext。\n *\n * 获取流程(各端一致,仅全局名不同):\n * canvas type=\"2d\" → 节点 node → node.getContext('2d') → ICanvas2D\n *\n * 节点获取依赖 SelectorQuery,属于组件生命周期职责,由\n * getMiniProgramCanvasNode() 提供;本文件只负责「节点 → 上下文」,\n * 以及 DPR 探测(pixelRatio 从各端系统信息接口读取)。\n */\n\nimport type { ChartRenderContext } from '../types';\nimport type { ICanvas2D } from '../core/canvas';\nimport { detectPixelRatio, type ResolveCanvasOptions } from './types';\n\n/** 小程序 Canvas 2D 节点(各端 node 的公共形状)。 */\nexport interface MiniProgramCanvasNode {\n getContext(type: '2d'): unknown;\n width: number;\n height: number;\n}\n\nexport type MiniProgramGlobal = 'wx' | 'my' | 'tt';\n\n/** 安全读取小程序全局(避免与官方类型定义冲突,模式同 transone request)。 */\nexport function getMiniProgramGlobal(\n name: MiniProgramGlobal\n): Record<string, unknown> | null {\n const host = getHost();\n const api = host[name];\n return typeof api === 'object' && api !== null\n ? (api as Record<string, unknown>)\n : null;\n}\n\n/**\n * 通过 SelectorQuery 获取 Canvas 2D 节点(组件/页面内调用)。\n * 各端 API 形状一致:createSelectorQuery().in(instance).select(selector)\n * .fields({ node: true, size: true }).exec(callback)\n */\nexport function getMiniProgramCanvasNode(options: {\n /** 端标识,缺省自动探测(wx → my → tt)。 */\n platform?: MiniProgramGlobal;\n /** 组件/页面实例(.in() 入参)。 */\n instance?: unknown;\n /** canvas 选择器,如 '#chart'。 */\n selector: string;\n}): Promise<MiniProgramCanvasNode> {\n const platform =\n options.platform ?? detectMiniProgramGlobal() ?? 'wx';\n const api = getMiniProgramGlobal(platform);\n const queryFn = api?.createSelectorQuery;\n if (typeof queryFn !== 'function') {\n return Promise.reject(\n new Error(`getMiniProgramCanvasNode: ${platform}.createSelectorQuery is not available`)\n );\n }\n\n return new Promise((resolve, reject) => {\n let query = queryFn.call(api);\n if (options.instance && typeof query.in === 'function') {\n query = query.in(options.instance);\n }\n if (typeof query.select !== 'function') {\n reject(new Error(`${platform}.createSelectorQuery().select is not available`));\n return;\n }\n query\n .select(options.selector)\n .fields({ node: true, size: true })\n .exec((res: unknown) => {\n const first = Array.isArray(res) ? res[0] : undefined;\n const node = first?.node as MiniProgramCanvasNode | undefined;\n if (!node || typeof node.getContext !== 'function') {\n reject(\n new Error(\n `getMiniProgramCanvasNode: canvas node not found for \"${options.selector}\"`\n )\n );\n return;\n }\n resolve(node);\n });\n });\n}\n\n/** 探测当前小程序全局(wx → my → tt),均不存在返回 null。 */\nexport function detectMiniProgramGlobal(): MiniProgramGlobal | null {\n if (getMiniProgramGlobal('wx')) {\n return 'wx';\n }\n if (getMiniProgramGlobal('my')) {\n return 'my';\n }\n if (getMiniProgramGlobal('tt')) {\n return 'tt';\n }\n return null;\n}\n\n/** 读取各端设备像素比(wx.getSystemInfoSync / my.getSystemInfoSync / tt.getSystemInfoSync)。 */\nexport function detectMiniProgramPixelRatio(platform?: MiniProgramGlobal): number {\n const name = platform ?? detectMiniProgramGlobal() ?? 'wx';\n const api = getMiniProgramGlobal(name);\n const sync = api?.getSystemInfoSync;\n if (typeof sync === 'function') {\n try {\n const info = sync.call(api) as { pixelRatio?: number };\n if (typeof info.pixelRatio === 'number' && info.pixelRatio > 0) {\n return info.pixelRatio;\n }\n } catch {\n // 探测失败回退通用探测\n }\n }\n return detectPixelRatio();\n}\n\n/**\n * 从 Canvas 2D 节点解析渲染上下文。\n * 物理画布尺寸由节点自身管理(width/height 已在平台侧按像素比设置),\n * 此处只做归一化返回。\n */\nexport function resolveMiniProgramCanvas(\n node: MiniProgramCanvasNode,\n options: ResolveCanvasOptions = {}\n): ChartRenderContext {\n const ctx2d = node.getContext('2d');\n if (!ctx2d) {\n throw new Error('resolveMiniProgramCanvas: 2d context is not available');\n }\n\n const dpr = options.dpr ?? detectMiniProgramPixelRatio();\n const width = options.width ?? node.width;\n const height = options.height ?? node.height;\n const ctx = ctx2d as unknown as ICanvas2D;\n\n return { ctx, width, height, dpr, palette: options.palette };\n}\n\nfunction getHost(): Record<string, unknown> {\n if (typeof globalThis !== 'undefined') {\n return globalThis as unknown as Record<string, unknown>;\n }\n return {};\n}\n",
|
|
19
|
+
"/**\n * Web 适配器:HTMLCanvasElement → ChartRenderContext。\n *\n * - 把物理画布尺寸设为 逻辑尺寸 × DPR,保证高分屏清晰\n * - 上下文为标准 CanvasRenderingContext2D,天然满足 ICanvas2D 契约\n */\n\nimport type { ChartRenderContext } from '../types';\nimport type { ICanvas2D } from '../core/canvas';\nimport { detectPixelRatio, type ResolveCanvasOptions } from './types';\n\n/**\n * 从 HTMLCanvasElement 解析渲染上下文。\n * 会就地修改 canvas.width / canvas.height 为物理像素尺寸。\n */\nexport function resolveWebCanvas(\n canvas: HTMLCanvasElement,\n options: ResolveCanvasOptions = {}\n): ChartRenderContext {\n if (!canvas) {\n throw new Error('resolveWebCanvas: canvas element is required');\n }\n\n const ctx2d = canvas.getContext('2d');\n if (!ctx2d) {\n throw new Error('resolveWebCanvas: 2d context is not available');\n }\n\n const dpr = options.dpr ?? detectPixelRatio();\n const width = options.width ?? (canvas.clientWidth || canvas.width);\n const height = options.height ?? (canvas.clientHeight || canvas.height);\n\n // 物理像素 = 逻辑像素 × DPR\n canvas.width = Math.round(width * dpr);\n canvas.height = Math.round(height * dpr);\n\n // 标准 CanvasRenderingContext2D 已满足 ICanvas2D 全部成员\n const ctx = ctx2d as unknown as ICanvas2D;\n\n return { ctx, width, height, dpr, palette: options.palette };\n}\n",
|
|
20
|
+
"/**\n * 图表工厂:按 option.type 分发到具体图表策略类(策略模式入口)。\n *\n * 用法(Web):\n * const chart = createWebChart(canvasEl, { type: 'line', ... });\n * chart.render();\n *\n * 用法(小程序,页面 onReady 后):\n * const node = await getMiniProgramCanvasNode({ selector: '#chart' });\n * const chart = createMiniProgramChart(node, option);\n * chart.render();\n */\n\nimport type { ChartOption, ChartRenderContext } from './types';\nimport type { ChartBase } from './core/chart';\nimport { LineChart } from './charts/line';\nimport { BarChart } from './charts/bar';\nimport { PieChart } from './charts/pie';\nimport { RadarChart } from './charts/radar';\nimport { resolveMiniProgramCanvas } from './adapters/miniprogram';\nimport type { MiniProgramCanvasNode } from './adapters/miniprogram';\nimport { resolveWebCanvas } from './adapters/web';\nimport type { ResolveCanvasOptions } from './adapters/types';\n\n/** 按类型创建图表实例(引擎核心入口)。 */\nexport function createChart<T extends ChartOption>(\n context: ChartRenderContext,\n option: T\n): ChartBase<T> {\n switch (option.type) {\n case 'line':\n return new LineChart(context, option) as unknown as ChartBase<T>;\n case 'bar':\n return new BarChart(context, option) as unknown as ChartBase<T>;\n case 'pie':\n return new PieChart(context, option) as unknown as ChartBase<T>;\n case 'radar':\n return new RadarChart(context, option) as unknown as ChartBase<T>;\n default:\n throw new Error(\n `createChart: unsupported chart type \"${(option as ChartOption).type}\"`\n );\n }\n}\n\n/** Web 便捷入口:从 HTMLCanvasElement 直接创建并渲染。 */\nexport function createWebChart<T extends ChartOption>(\n canvas: HTMLCanvasElement,\n option: T,\n resolveOptions: ResolveCanvasOptions = {}\n): ChartBase<T> {\n return createChart(resolveWebCanvas(canvas, resolveOptions), option);\n}\n\n/** 小程序便捷入口:从 Canvas 2D 节点直接创建并渲染。 */\nexport function createMiniProgramChart<T extends ChartOption>(\n node: MiniProgramCanvasNode,\n option: T,\n resolveOptions: ResolveCanvasOptions = {}\n): ChartBase<T> {\n return createChart(resolveMiniProgramCanvas(node, resolveOptions), option);\n}\n",
|
|
21
|
+
"/**\n * 原生 App 适配器(占位 / 契约声明)。\n *\n * 未来 iOS / Android / 鸿蒙 原生端接入路径:\n * 1. 宿主提供原生 canvas 能力(Skia / ArkUI Canvas / WebView 桥),\n * 把原生绘制 API 实现为 ICanvas2D(见 core/canvas.ts 契约)\n * 2. 实现 resolveNativeCanvas(),返回 ChartRenderContext(含 DPR 与逻辑尺寸)\n * 3. 之后引擎与四种图表零改动运行\n *\n * 本文件当前不导出可运行实现,仅保留类型契约与接入文档,防止\n * 平台差异悄悄渗入引擎。\n */\n\nimport type { ChartRenderContext } from '../types';\n\n/** 未来原生端需实现的最小宿主接口。 */\nexport interface NativeCanvasHost {\n /** 逻辑尺寸(CSS px)。 */\n width: number;\n height: number;\n /** 设备像素比。 */\n pixelRatio: number;\n /** 原生绘图上下文(宿主实现为 ICanvas2D 契约)。 */\n getContext(type: '2d'): unknown;\n}\n\n/**\n * 未来实现:原生 canvas 节点 → ChartRenderContext。\n * 当前未接入任何原生端,调用会明确报错,避免静默降级。\n */\nexport function resolveNativeCanvas(\n _host: NativeCanvasHost\n): ChartRenderContext {\n throw new Error(\n 'resolveNativeCanvas is not implemented yet. ' +\n 'Native app support (iOS/Android/HarmonyOS) is planned for a future phase; ' +\n 'implement ICanvas2D on the host side and fill in this resolver.'\n );\n}\n",
|
|
22
|
+
"import type {\n AnyComponentConstructor,\n ComponentConstructor,\n ComponentEventListener,\n ComponentProps,\n} from './component';\nimport type { ModelBinding } from './model';\n\nexport interface ComponentType {\n mount(container: HTMLElement): void;\n unmount(): void;\n on(eventName: string, listener: ComponentEventListener): () => void;\n}\n\ntype VNodeComponentProps = ComponentProps;\n\nexport type VNodeComponentConstructor<\n P extends VNodeComponentProps = VNodeComponentProps,\n> = ComponentConstructor<P> | AnyComponentConstructor;\n\nexport type HTMLPropValue =\n | string\n | number\n | boolean\n | null\n | undefined\n | Record<string, string | number>\n | EventListener;\n\nexport type Children = VNode | string | Array<VNode | string>;\n\n/** 归一化 children 为数组(运行时渲染使用;h() 允许直接传单个节点或数组)。 */\nexport function toChildren(children?: Children): Array<VNode | string> {\n if (children === undefined || children === null) {\n return [];\n }\n return Array.isArray(children) ? children : [children];\n}\n\nexport interface HTMLProps {\n [key: string]: HTMLPropValue;\n class?: string;\n className?: string;\n style?: Record<string, string | number>;\n}\n\nexport interface EventListeners {\n [eventName: string]: (event: Event) => void;\n}\n\nexport interface Directions {\n model?: ModelBinding;\n if?: boolean;\n show?: boolean;\n}\n\nexport interface VNodeBase {\n key?: string | number;\n slot?: string;\n directions?: Directions;\n}\n\nexport interface HTMLNode extends VNodeBase {\n tag: string;\n props?: HTMLProps;\n children?: Children;\n listeners?: EventListeners;\n}\n\nexport interface ComponentNode<\n P extends VNodeComponentProps = VNodeComponentProps,\n> extends VNodeBase {\n component: VNodeComponentConstructor<P>;\n props?: P;\n children?: Children;\n emitters?: Record<string, ComponentEventListener>;\n}\n\nexport interface SlotProvider extends VNodeBase {\n tag: 'slot';\n props: { name: string };\n children?: Array<VNode | string>;\n}\n\nexport interface SlotInjector extends VNodeBase {\n tag: string;\n slot: string;\n}\n\n// 渲染树节点:组件节点 props 各异(如 CardProps),统一放宽为任意 props 形态\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type VNode = HTMLNode | ComponentNode<any> | SlotProvider | SlotInjector;\n\nexport type ElementShortcutOptions = Omit<HTMLNode, 'tag'>;\n\nexport type ElementShortcut = (options?: ElementShortcutOptions) => HTMLNode;\n\nexport function isComponentNode(vnode: VNode): vnode is ComponentNode {\n return typeof vnode === 'object' && vnode !== null && 'component' in vnode;\n}\n\nexport function isHTMLNode(vnode: VNode): vnode is HTMLNode {\n return (\n typeof vnode === 'object' &&\n vnode !== null &&\n 'tag' in vnode &&\n vnode.tag !== 'slot'\n );\n}\n\nexport function isSlotProvider(vnode: VNode): vnode is SlotProvider {\n return (\n typeof vnode === 'object' &&\n vnode !== null &&\n 'tag' in vnode &&\n vnode.tag === 'slot'\n );\n}\n\nexport function h(\n tag: string,\n props?: HTMLProps,\n children?: Children,\n listeners?: EventListeners,\n key?: string | number,\n directions?: Directions\n): HTMLNode {\n return {\n tag,\n props,\n children,\n listeners,\n key,\n directions,\n };\n}\n\nexport function Tag(\n tag: string,\n options: ElementShortcutOptions = {}\n): HTMLNode {\n return {\n tag,\n ...options,\n };\n}\n\nfunction createElementFactory(tag: string): ElementShortcut {\n return (options: ElementShortcutOptions = {}) => Tag(tag, options);\n}\n\nexport const Div = createElementFactory('div');\nexport const Span = createElementFactory('span');\nexport const P = createElementFactory('p');\nexport const Button = createElementFactory('button');\nexport const Input = createElementFactory('input');\nexport const Section = createElementFactory('section');\nexport const Main = createElementFactory('main');\nexport const Header = createElementFactory('header');\nexport const Footer = createElementFactory('footer');\nexport const Nav = createElementFactory('nav');\nexport const Article = createElementFactory('article');\nexport const Aside = createElementFactory('aside');\nexport const H1 = createElementFactory('h1');\nexport const H2 = createElementFactory('h2');\nexport const H3 = createElementFactory('h3');\nexport const H4 = createElementFactory('h4');\nexport const H5 = createElementFactory('h5');\nexport const H6 = createElementFactory('h6');\nexport const Strong = createElementFactory('strong');\nexport const Em = createElementFactory('em');\nexport const Small = createElementFactory('small');\nexport const Pre = createElementFactory('pre');\nexport const Code = createElementFactory('code');\nexport const Blockquote = createElementFactory('blockquote');\nexport const Ul = createElementFactory('ul');\nexport const Ol = createElementFactory('ol');\nexport const Li = createElementFactory('li');\nexport const A = createElementFactory('a');\nexport const Img = createElementFactory('img');\nexport const Form = createElementFactory('form');\nexport const Label = createElementFactory('label');\nexport const Textarea = createElementFactory('textarea');\nexport const Select = createElementFactory('select');\nexport const Option = createElementFactory('option');\nexport const Table = createElementFactory('table');\nexport const Thead = createElementFactory('thead');\nexport const Tbody = createElementFactory('tbody');\nexport const Tr = createElementFactory('tr');\nexport const Th = createElementFactory('th');\nexport const Td = createElementFactory('td');\n\nexport interface ComponentDescriptor<P extends VNodeComponentProps> {\n component: ComponentConstructor<P>;\n props?: P;\n children?: Children;\n key?: string | number;\n directions?: Directions;\n emitters?: Record<string, ComponentEventListener>;\n}\n\nexport function createComponent<P extends VNodeComponentProps>(\n options: ComponentDescriptor<P>\n): ComponentNode<P>;\nexport function createComponent<P extends VNodeComponentProps>(\n componentClass: ComponentConstructor<P>,\n props?: P,\n children?: Children,\n key?: string | number,\n directions?: Directions\n): ComponentNode<P>;\nexport function createComponent<P extends VNodeComponentProps>(\n componentOrOptions: ComponentConstructor<P> | ComponentDescriptor<P>,\n props?: P,\n children?: Children,\n key?: string | number,\n directions?: Directions\n): ComponentNode<P> {\n if (typeof componentOrOptions === 'function') {\n return {\n component: componentOrOptions,\n props,\n children,\n key,\n directions,\n };\n }\n return {\n component: componentOrOptions.component,\n props: componentOrOptions.props,\n children: componentOrOptions.children,\n key: componentOrOptions.key,\n directions: componentOrOptions.directions,\n emitters: componentOrOptions.emitters,\n };\n}\n\nexport function slot(\n name: string,\n key?: string | number,\n directions?: Directions\n): SlotProvider {\n return {\n tag: 'slot',\n props: { name },\n key,\n directions,\n };\n}\n\nexport function each<T>(\n items: readonly T[],\n render: (item: T, index: number) => VNode | string,\n key?: (item: T, index: number) => string | number\n): VNode[] {\n return items.map((item, index) => {\n const vnode = render(item, index);\n if (typeof vnode === 'string') {\n throw new Error('each render callback must return a VNode');\n }\n if (!key) {\n return vnode;\n }\n\n return { ...vnode, key: key(item, index) };\n });\n}\n",
|
|
23
|
+
"export interface ReactiveEffect<T = unknown> {\n (): T | undefined;\n deps: Set<ReactiveEffect>[];\n id: number;\n active: boolean;\n scheduler?: (effect: ReactiveEffect) => void;\n}\n\nexport interface ReactiveEffectOptions {\n lazy?: boolean;\n scheduler?: (effect: ReactiveEffect) => void;\n throwOnError?: boolean;\n}\n\nexport interface ComputedRef<T> {\n readonly value: T;\n}\n\nexport const IS_REACTIVE = Symbol('is_reactive');\nexport const IS_READONLY = Symbol('is_readonly');\nexport const IS_REF = Symbol('is_ref');\n\nexport interface Ref<T = unknown> {\n value: T;\n}\n\nexport const MUTATING_ARRAY_METHODS = [\n 'push',\n 'pop',\n 'shift',\n 'unshift',\n 'splice',\n 'sort',\n 'reverse',\n] as const;\n\nexport function hasReactiveFlag(\n value: object,\n flag: typeof IS_REACTIVE | typeof IS_READONLY\n): boolean {\n return Boolean(Reflect.get(value, flag));\n}\n\nexport function isObject(value: unknown): value is object {\n return value !== null && typeof value === 'object';\n}\n",
|
|
24
|
+
"import type { ReactiveEffect } from './types';\n\n/**\n * 响应式 effect 批处理调度器。\n *\n * 同一批同步状态变更(同一个任务内的多次 mutation)只会触发一次 effect\n * 运行:effect 被 enqueue 后去重入队,并在微任务中统一冲刷(flush)。\n * 冲刷期间的重复入队会在同一轮循环中继续处理,直到队列清空。\n *\n * 组件渲染通过该调度器合并更新:连续修改 state 不会逐次重渲染,\n * 与 Vue 微任务调度 / React 自动批处理的语义一致。\n */\nexport class ReactiveScheduler {\n private readonly pending = new Set<ReactiveEffect>();\n private flushing = false;\n private flushPromise: Promise<void> | null = null;\n private flushResolve: (() => void) | null = null;\n\n /**\n * 将 effect 加入待冲刷队列(去重)。\n */\n public enqueue(effect: ReactiveEffect): void {\n if (this.pending.has(effect)) {\n return;\n }\n this.pending.add(effect);\n if (!this.flushing) {\n queueMicrotask(() => this.flush());\n }\n }\n\n /**\n * 同步冲刷队列:立即执行所有待运行的 effect。\n * 若当前正处于一次冲刷中(例如在 effect 内部调用),则直接返回,\n * 由正在进行的冲刷循环负责处理新增任务。\n */\n public flush(): void {\n if (this.flushing) {\n return;\n }\n this.flushing = true;\n try {\n while (this.pending.size > 0) {\n const batch = Array.from(this.pending);\n this.pending.clear();\n for (const effect of batch) {\n if (effect.active) {\n effect();\n }\n }\n }\n } finally {\n this.flushing = false;\n const resolve = this.flushResolve;\n this.flushResolve = null;\n this.flushPromise = null;\n resolve?.();\n }\n }\n\n /**\n * 返回一个在待冲刷的 effect 全部执行完成后 resolve 的 Promise。\n * 若当前没有待冲刷任务,立即 resolve。\n */\n public nextTick(): Promise<void> {\n if (!this.flushing && this.pending.size === 0) {\n return Promise.resolve();\n }\n if (this.flushPromise) {\n return this.flushPromise;\n }\n this.flushPromise = new Promise<void>((resolve) => {\n this.flushResolve = resolve;\n });\n return this.flushPromise;\n }\n}\n",
|
|
25
|
+
"import {\n ComputedRef,\n IS_REACTIVE,\n IS_REF,\n IS_READONLY,\n MUTATING_ARRAY_METHODS,\n Ref,\n ReactiveEffect,\n ReactiveEffectOptions,\n hasReactiveFlag,\n isObject,\n} from './reactive/types';\nimport { ReactiveScheduler } from './reactive/scheduler';\n\nexport type {\n ComputedRef,\n Ref,\n ReactiveEffect,\n ReactiveEffectOptions,\n} from './reactive/types';\n\nlet effectId = 0;\n\n// 全局批处理调度器:组件渲染等场景通过它合并同一批同步状态变更\nexport const reactiveScheduler = new ReactiveScheduler();\n\n/**\n * 等待当前批次的响应式 effect 全部执行完成(通常是组件重渲染)。\n * 修改 state 后需要立即读取 DOM 时使用:await nextTick()。\n */\nexport function nextTick(): Promise<void> {\n return reactiveScheduler.nextTick();\n}\n\n/**\n * 同步冲刷待执行的响应式 effect,立即完成组件重渲染。\n * 一般仅测试或需要同步读 DOM 的场景使用。\n */\nexport function flushSync(): void {\n reactiveScheduler.flush();\n}\n\n// 响应式系统\nexport class ReactiveSystem {\n private static instance: ReactiveSystem;\n private activeEffect: ReactiveEffect | null = null;\n private readonly effectStack: ReactiveEffect[] = [];\n private targetMap = new WeakMap<\n object,\n Map<string | symbol, Set<ReactiveEffect>>\n >();\n private reactiveMap = new WeakMap<object, object>();\n private readonlyMap = new WeakMap<object, object>();\n /** 反向映射:响应式代理 -> 原始对象,供 toRawValue 反查 */\n private proxyMap = new WeakMap<object, object>();\n\n private constructor() {}\n\n public static getInstance(): ReactiveSystem {\n if (!ReactiveSystem.instance) {\n ReactiveSystem.instance = new ReactiveSystem();\n }\n return ReactiveSystem.instance;\n }\n\n public reactive<T extends object>(target: T): T {\n // 检查输入参数\n if (!isObject(target)) {\n console.warn('reactive: target must be an object');\n return target;\n }\n\n // 如果传入的是已经是响应式对象,直接返回\n if (isReactive(target)) {\n return target;\n }\n\n // 如果目标已经有对应的响应式对象,返回已存在的代理\n if (this.reactiveMap.has(target)) {\n return this.reactiveMap.get(target) as T;\n }\n\n // 优化数组处理\n if (Array.isArray(target)) {\n return this.createReactiveArray(target);\n }\n\n // 普通对象处理\n const proxy = new Proxy(target, {\n get: (target, key: string | symbol) => {\n // 处理响应式标记的特殊属性\n if (key === IS_REACTIVE) {\n return true;\n }\n if (key === IS_READONLY) {\n return false;\n }\n\n // 收集依赖\n this.track(target, key);\n\n const value = Reflect.get(target, key);\n\n // 如果是对象,递归转换为响应式\n if (isObject(value) && !hasReactiveFlag(value, IS_READONLY)) {\n return this.reactive(value);\n }\n return value;\n },\n set: (target, key: string | symbol, value) => {\n // 检查是否是只读的\n if (hasReactiveFlag(target, IS_READONLY)) {\n console.warn(`Cannot set property ${String(key)} on readonly object`);\n return false;\n }\n\n const oldValue = Reflect.get(target, key);\n\n // 如果新值是对象,转换为响应式\n if (\n isObject(value) &&\n !hasReactiveFlag(value, IS_REACTIVE) &&\n !hasReactiveFlag(value, IS_READONLY)\n ) {\n value = this.reactive(value);\n }\n\n const result = Reflect.set(target, key, value);\n\n // 只有当值真正改变时才触发更新\n if (oldValue !== value) {\n this.trigger(target, key);\n }\n return result;\n },\n deleteProperty: (target, key: string | symbol) => {\n // 检查是否是只读的\n if (hasReactiveFlag(target, IS_READONLY)) {\n console.warn(\n `Cannot delete property ${String(key)} on readonly object`\n );\n return false;\n }\n\n const hadKey = key in target;\n const result = Reflect.deleteProperty(target, key);\n\n if (hadKey) {\n this.trigger(target, key);\n }\n return result;\n },\n });\n\n // 存储响应式对象的映射关系\n this.reactiveMap.set(target, proxy);\n this.proxyMap.set(proxy, target);\n return proxy;\n }\n\n /**\n * 将响应式代理反查为原始对象;非代理值原样返回。\n * 用于数组 includes/indexOf/lastIndexOf 的对比,让用户传入\n * 原始对象也能命中数组中的响应式元素。\n */\n private toRawValue(value: unknown): unknown {\n if (isObject(value) && this.proxyMap.has(value)) {\n return this.proxyMap.get(value);\n }\n return value;\n }\n\n /**\n * 创建响应式数组\n */\n private createReactiveArray<T extends unknown[]>(target: T): T {\n // 如果目标已经有对应的响应式对象,返回已存在的代理\n if (this.reactiveMap.has(target)) {\n return this.reactiveMap.get(target) as T;\n }\n\n const proxy = new Proxy(target, {\n get: (target, key: string | symbol) => {\n // 处理响应式标记的特殊属性\n if (key === IS_REACTIVE) {\n return true;\n }\n if (key === IS_READONLY) {\n return false;\n }\n\n // 收集依赖\n this.track(target, key);\n\n const value = Reflect.get(target, key);\n\n // 搜索类方法:数组元素是响应式代理,需把参数与元素都转回原始值\n // 后再对比,保证 includes(rawObject) / indexOf(rawObject) 能命中。\n if (\n typeof key === 'string' &&\n (key === 'includes' || key === 'indexOf' || key === 'lastIndexOf')\n ) {\n // 数组内容(长度与索引)变化会改变搜索方法的返回结果\n this.track(target, 'length');\n for (let i = 0; i < target.length; i++) {\n this.track(target, String(i));\n }\n const method = value as (...methodArgs: unknown[]) => unknown;\n return (...args: unknown[]) => {\n const result = method.apply(target, args);\n if (result === false || result === -1) {\n const rawElements = target.map((item) => this.toRawValue(item));\n const fallbackMethod = rawElements[key] as (\n ...methodArgs: unknown[]\n ) => unknown;\n return fallbackMethod.apply(\n rawElements,\n args.map((arg) => this.toRawValue(arg))\n );\n }\n return result;\n };\n }\n\n // 处理数组的变异方法\n if (\n typeof key === 'string' &&\n MUTATING_ARRAY_METHODS.includes(\n key as (typeof MUTATING_ARRAY_METHODS)[number]\n )\n ) {\n return (...args: unknown[]) => {\n // 执行原始方法\n const arrayMethod = value as (...methodArgs: unknown[]) => unknown;\n const result = arrayMethod.apply(target, args);\n // 触发数组更新\n this.trigger(target, 'length');\n this.trigger(target, key);\n return result;\n };\n }\n\n if (isObject(value) && !hasReactiveFlag(value, IS_READONLY)) {\n return this.reactive(value);\n }\n\n return value;\n },\n set: (target, key: string | symbol, value) => {\n // 检查是否是只读的\n if (hasReactiveFlag(target, IS_READONLY)) {\n console.warn(`Cannot set property ${String(key)} on readonly object`);\n return false;\n }\n\n const oldValue = Reflect.get(target, key);\n\n // 如果新值是对象,转换为响应式\n if (\n isObject(value) &&\n !hasReactiveFlag(value, IS_REACTIVE) &&\n !hasReactiveFlag(value, IS_READONLY)\n ) {\n value = this.reactive(value);\n }\n\n const result = Reflect.set(target, key, value);\n\n // 只有当值真正改变时才触发更新\n if (oldValue !== value) {\n this.trigger(target, key);\n // 对于数组索引的修改,同时触发length的更新\n if (typeof key === 'string' && !isNaN(Number(key))) {\n this.trigger(target, 'length');\n }\n }\n return result;\n },\n deleteProperty: (target, key: string | symbol) => {\n // 检查是否是只读的\n if (hasReactiveFlag(target, IS_READONLY)) {\n console.warn(\n `Cannot delete property ${String(key)} on readonly object`\n );\n return false;\n }\n\n const hadKey = key in target;\n const result = Reflect.deleteProperty(target, key);\n\n if (hadKey) {\n this.trigger(target, key);\n // 对于数组,同时触发length的更新\n this.trigger(target, 'length');\n }\n return result;\n },\n });\n\n // 存储响应式对象的映射关系\n this.reactiveMap.set(target, proxy);\n this.proxyMap.set(proxy, target);\n return proxy;\n }\n\n public readonly<T extends object>(target: T): Readonly<T> {\n // 检查输入参数\n if (!isObject(target)) {\n console.warn('readonly: target must be an object');\n return target;\n }\n\n // 如果传入的是已经是只读响应式对象,直接返回\n if (hasReactiveFlag(target, IS_READONLY)) {\n return target as Readonly<T>;\n }\n\n // 如果目标已经有对应的只读响应式对象,返回已存在的代理\n if (this.readonlyMap.has(target)) {\n return this.readonlyMap.get(target) as Readonly<T>;\n }\n\n const proxy = new Proxy(target, {\n get: (target, key: string | symbol) => {\n // 处理响应式标记的特殊属性\n if (key === IS_REACTIVE) {\n return false;\n }\n if (key === IS_READONLY) {\n return true;\n }\n\n const value = Reflect.get(target, key);\n // 如果是对象,递归转换为只读响应式\n if (isObject(value)) {\n return this.readonly(value);\n }\n return value;\n },\n set: () => {\n console.warn('Cannot set property on readonly object');\n return false;\n },\n deleteProperty: () => {\n console.warn('Cannot delete property on readonly object');\n return false;\n },\n });\n\n // 存储只读响应式对象的映射关系\n this.readonlyMap.set(target, proxy);\n return proxy as Readonly<T>;\n }\n\n public effect<T = unknown>(\n fn: () => T,\n options?: ReactiveEffectOptions\n ): ReactiveEffect<T> {\n const { lazy = false, scheduler, throwOnError = false } = options || {};\n\n const effectFn: ReactiveEffect<T> = () => {\n if (!effectFn.active) {\n return fn();\n }\n\n try {\n // 清除之前的依赖关系\n this.cleanup(effectFn);\n this.effectStack.push(effectFn);\n this.activeEffect = effectFn;\n return fn();\n } catch (error) {\n if (throwOnError) {\n throw error;\n }\n console.error('Effect error:', error);\n return undefined;\n } finally {\n this.effectStack.pop();\n this.activeEffect =\n this.effectStack[this.effectStack.length - 1] ?? null;\n }\n };\n\n effectFn.id = effectId++;\n effectFn.deps = [];\n effectFn.active = true;\n effectFn.scheduler = scheduler;\n\n // 如果不是懒加载,立即执行\n if (!lazy) {\n effectFn();\n }\n\n return effectFn;\n }\n\n /**\n * 在指定 effect 的上下文中运行 fn,使 fn 内的响应式访问被收集到该 effect,\n * 而不是当前外层 effect。组件渲染用它隔离依赖:子组件在父组件渲染期间\n * 执行自身 render 时,子 state 的访问不会污染父组件的 effect。\n */\n public runWithEffect<T>(effect: ReactiveEffect, fn: () => T): T {\n this.effectStack.push(effect);\n this.activeEffect = effect;\n try {\n return fn();\n } finally {\n this.effectStack.pop();\n this.activeEffect = this.effectStack[this.effectStack.length - 1] ?? null;\n }\n }\n\n public computed<T>(getter: () => T): ComputedRef<T> {\n let dirty = true;\n let value: T;\n const computedTarget = {};\n const trackComputedValue = (): void => {\n this.track(computedTarget, 'value');\n };\n\n const runner = this.effect(\n () => {\n value = getter();\n dirty = false;\n },\n {\n lazy: true,\n scheduler: () => {\n if (!dirty) {\n dirty = true;\n this.trigger(computedTarget, 'value');\n }\n },\n }\n );\n\n return {\n get value(): T {\n if (dirty) {\n runner();\n }\n trackComputedValue();\n return value as T;\n },\n };\n }\n\n private cleanup(effect: ReactiveEffect): void {\n // 清除effect的所有依赖\n effect.deps.forEach((dep) => {\n dep.delete(effect);\n });\n effect.deps.length = 0;\n }\n\n private track(target: object, key: string | symbol): void {\n if (!this.activeEffect || !this.activeEffect.active) return;\n\n let depsMap = this.targetMap.get(target);\n if (!depsMap) {\n depsMap = new Map();\n this.targetMap.set(target, depsMap);\n }\n\n let dep = depsMap.get(key);\n if (!dep) {\n dep = new Set();\n depsMap.set(key, dep);\n }\n\n if (!dep.has(this.activeEffect)) {\n dep.add(this.activeEffect);\n this.activeEffect.deps.push(dep);\n }\n }\n\n private trigger(target: object, key: string | symbol): void {\n const depsMap = this.targetMap.get(target);\n if (!depsMap) return;\n\n const dep = depsMap.get(key);\n if (!dep) return;\n\n // 创建依赖的副本进行遍历,避免在遍历过程中依赖发生变化\n const effects = new Set(dep);\n effects.forEach((effect) => {\n if (effect.active) {\n if (effect.scheduler) {\n effect.scheduler(effect);\n } else {\n effect();\n }\n }\n });\n }\n\n // 停止一个effect的执行\n public stop(effect: ReactiveEffect): void {\n if (effect.active) {\n this.cleanup(effect);\n effect.active = false;\n }\n }\n}\n\n// 导出响应式API\nexport function reactive<T extends object>(target: T): T {\n return ReactiveSystem.getInstance().reactive(target);\n}\n\nexport function readonly<T extends object>(target: T): Readonly<T> {\n return ReactiveSystem.getInstance().readonly(target);\n}\n\nexport function effect<T = unknown>(\n fn: () => T,\n options?: ReactiveEffectOptions\n): ReactiveEffect<T> {\n return ReactiveSystem.getInstance().effect(fn, options);\n}\n\nexport function computed<T>(getter: () => T): ComputedRef<T> {\n return ReactiveSystem.getInstance().computed(getter);\n}\n\nexport function ref<T>(value: T): Ref<T> {\n const wrapper = { value };\n Object.defineProperty(wrapper, IS_REF, {\n configurable: false,\n enumerable: false,\n value: true,\n });\n\n return reactive(wrapper) as Ref<T>;\n}\n\nexport function isRef(value: unknown): value is Ref<unknown> {\n return isObject(value) && Boolean(Reflect.get(value, IS_REF));\n}\n\nexport function unref<T>(value: T | Ref<T>): T {\n return isRef(value) ? value.value : value;\n}\n\nexport function stop(effect: ReactiveEffect): void {\n ReactiveSystem.getInstance().stop(effect);\n}\n\nexport interface WatchSource<T> {\n value: T;\n}\n\nexport type WatchCallback<T> = (\n value: T,\n oldValue: T | undefined,\n onCleanup: (cleanup: () => void) => void\n) => void;\n\nexport interface WatchOptions {\n /** 建立后立即执行一次回调(此时 oldValue 为 undefined) */\n immediate?: boolean;\n /** 深度追踪 getter 返回值的嵌套属性变化 */\n deep?: boolean;\n /** 变化发生时同步回调(默认随调度器批处理,同一批变化只回调一次) */\n sync?: boolean;\n}\n\n/**\n * 深度遍历响应式值,在 effect 上下文中访问全部 key 以收集嵌套依赖。\n */\nfunction traverseReactive(value: unknown, seen: Set<object>): void {\n if (!isObject(value) || seen.has(value)) {\n return;\n }\n\n seen.add(value);\n\n if (Array.isArray(value)) {\n value.forEach((item) => traverseReactive(item, seen));\n return;\n }\n\n Object.keys(value).forEach((key) => {\n traverseReactive((value as Record<string, unknown>)[key], seen);\n });\n}\n\n/**\n * 侦听响应式源(getter 或 ref)的变化并执行回调。\n * 返回一个用于停止侦听的函数。\n */\nexport function watch<T>(\n source: WatchSource<T> | (() => T),\n callback: WatchCallback<T>,\n options: WatchOptions = {}\n): () => void {\n const system = ReactiveSystem.getInstance();\n const getter = isRef(source)\n ? (): T => (source as Ref<T>).value\n : (source as () => T);\n\n let oldValue: T | undefined;\n let cleanup: (() => void) | null = null;\n\n const run = (): void => {\n if (!job.active) {\n return;\n }\n cleanup?.();\n cleanup = null;\n // runner 为 lazy effect,active 时调用必然返回 getter 值\n const newValue = runner() as T;\n callback(newValue, oldValue, (fn) => {\n cleanup = fn;\n });\n oldValue = newValue;\n };\n\n const runner = effect(\n (): T => {\n const value = getter();\n if (options.deep && isObject(value)) {\n traverseReactive(value, new Set<object>());\n }\n return value;\n },\n {\n lazy: true,\n scheduler: () => {\n if (options.sync) {\n run();\n } else {\n reactiveScheduler.enqueue(job);\n }\n },\n }\n );\n\n // 批处理队列中的执行单元:调度器 flush 时通过它完成求值与回调\n const job = effect(\n () => {\n run();\n },\n { lazy: true }\n );\n\n if (options.immediate) {\n run();\n } else {\n oldValue = runner();\n }\n\n return () => {\n system.stop(runner);\n system.stop(job);\n };\n}\n\n// 工具函数:判断是否是响应式对象\nexport function isReactive(value: unknown): boolean {\n return isObject(value) && hasReactiveFlag(value, IS_REACTIVE);\n}\n\n// 工具函数:判断是否是只读响应式对象\nexport function isReadonly(value: unknown): boolean {\n return isObject(value) && hasReactiveFlag(value, IS_READONLY);\n}\n",
|
|
26
|
+
"import { effect, stop, type ReactiveEffect } from './reactive';\n\nexport interface ModelBindingOptions {\n path: string;\n parse?: (value: unknown) => unknown;\n format?: (value: unknown) => string;\n}\n\nexport type ModelBinding = string | ModelBindingOptions;\n\ntype ModelState = Record<string, unknown>;\n\ninterface BindingRecord {\n binding: ModelBinding;\n path: string;\n parse?: (value: unknown) => unknown;\n format?: (value: unknown) => string;\n eventName: 'input' | 'change';\n listener: EventListener;\n effect: ReactiveEffect;\n}\n\nfunction pathSegments(path: string): string[] {\n const segments = path.split('.');\n if (\n path.length === 0 ||\n segments.some(\n (segment) =>\n segment.length === 0 ||\n segment === '__proto__' ||\n segment === 'prototype' ||\n segment === 'constructor'\n )\n ) {\n throw new Error(`Invalid model path \"${path}\"`);\n }\n return segments;\n}\n\nfunction isRecord(value: unknown): value is ModelState {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction hasOwn(value: object, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(value, key);\n}\n\nexport function modelPath(binding: ModelBinding): string {\n return typeof binding === 'string' ? binding : binding.path;\n}\n\nexport function getModelValue(state: ModelState, path: string): unknown {\n let value: unknown = state;\n\n for (const segment of pathSegments(path)) {\n if (!isRecord(value)) {\n throw new Error(`Invalid model path \"${path}\"`);\n }\n if (!hasOwn(value, segment)) {\n if (segment in value) {\n throw new Error(`Invalid model path \"${path}\"`);\n }\n return undefined;\n }\n value = value[segment];\n }\n\n return value;\n}\n\nexport function setModelValue(\n state: ModelState,\n path: string,\n value: unknown\n): void {\n const segments = pathSegments(path);\n let target: ModelState = state;\n\n for (const segment of segments.slice(0, -1)) {\n if (!hasOwn(target, segment)) {\n if (segment in target) {\n throw new Error(`Invalid model path \"${path}\"`);\n }\n target[segment] = {};\n } else if (!isRecord(target[segment])) {\n throw new Error(`Invalid model path \"${path}\"`);\n }\n const nextTarget = target[segment];\n if (!isRecord(nextTarget)) {\n throw new Error(`Invalid model path \"${path}\"`);\n }\n target = nextTarget;\n }\n\n const lastSegment = segments[segments.length - 1];\n target[lastSegment] = value;\n}\n\nfunction displayValue(binding: ModelBinding, value: unknown): string {\n if (typeof binding !== 'string' && binding.format) {\n return binding.format(value);\n }\n return value === undefined || value === null ? '' : String(value);\n}\n\nfunction toModelValue(binding: ModelBinding, value: unknown): unknown {\n if (typeof binding !== 'string' && binding.parse) {\n return binding.parse(value);\n }\n return value;\n}\n\nfunction syncControl(\n element: HTMLElement,\n binding: ModelBinding,\n value: unknown\n): void {\n if (element instanceof HTMLInputElement) {\n if (element.type === 'checkbox') {\n element.checked = Array.isArray(value)\n ? value.some((item) => String(item) === element.value)\n : Boolean(value);\n return;\n }\n if (element.type === 'radio') {\n element.checked = value === element.value;\n return;\n }\n element.value = displayValue(binding, value);\n return;\n }\n\n if (element instanceof HTMLTextAreaElement) {\n element.value = displayValue(binding, value);\n return;\n }\n\n if (element instanceof HTMLSelectElement) {\n if (element.multiple) {\n const selected = Array.isArray(value)\n ? new Set(value.map(String))\n : new Set<string>();\n for (let index = 0; index < element.options.length; index += 1) {\n const option = element.options.item(index);\n if (!option) {\n continue;\n }\n option.selected = selected.has(option.value);\n }\n return;\n }\n element.value = displayValue(binding, value);\n }\n}\n\nfunction controlValue(element: HTMLElement, currentValue: unknown): unknown {\n if (element instanceof HTMLInputElement) {\n if (element.type === 'checkbox') {\n if (Array.isArray(currentValue)) {\n const values = currentValue.filter(\n (value) => String(value) !== element.value\n );\n return element.checked ? [...values, element.value] : values;\n }\n return element.checked;\n }\n if (element.type === 'radio') {\n return element.checked ? element.value : currentValue;\n }\n return element.value;\n }\n if (element instanceof HTMLTextAreaElement) {\n return element.value;\n }\n if (element instanceof HTMLSelectElement) {\n if (!element.multiple) {\n return element.value;\n }\n const values: string[] = [];\n for (let index = 0; index < element.selectedOptions.length; index += 1) {\n const option = element.selectedOptions.item(index);\n if (option) {\n values.push(option.value);\n }\n }\n return values;\n }\n return undefined;\n}\n\nexport class ModelBindingController {\n private readonly bindings = new WeakMap<HTMLElement, BindingRecord>();\n\n public bind(\n element: HTMLElement,\n binding: ModelBinding,\n state: ModelState\n ): void {\n if (!this.isSupportedControl(element)) {\n return;\n }\n\n const existing = this.bindings.get(element);\n if (existing && this.sameBinding(existing, binding)) {\n return;\n }\n this.cleanup(element);\n\n const path = modelPath(binding);\n const sync = (): void =>\n syncControl(element, binding, getModelValue(state, path));\n const eventName =\n element instanceof HTMLTextAreaElement ||\n (element instanceof HTMLInputElement &&\n !['checkbox', 'radio'].includes(element.type))\n ? 'input'\n : 'change';\n const listener = (): void => {\n const currentValue = getModelValue(state, path);\n setModelValue(\n state,\n path,\n toModelValue(binding, controlValue(element, currentValue))\n );\n };\n\n element.addEventListener(eventName, listener);\n const effectRef = effect(sync);\n this.bindings.set(element, {\n binding,\n path,\n parse: typeof binding === 'string' ? undefined : binding.parse,\n format: typeof binding === 'string' ? undefined : binding.format,\n eventName,\n listener,\n effect: effectRef,\n });\n }\n\n public cleanup(element: HTMLElement): void {\n const existing = this.bindings.get(element);\n if (!existing) {\n return;\n }\n element.removeEventListener(existing.eventName, existing.listener);\n stop(existing.effect);\n this.bindings.delete(element);\n }\n\n private isSupportedControl(element: HTMLElement): boolean {\n return (\n element instanceof HTMLInputElement ||\n element instanceof HTMLTextAreaElement ||\n element instanceof HTMLSelectElement\n );\n }\n\n private sameBinding(record: BindingRecord, binding: ModelBinding): boolean {\n if (typeof record.binding === 'string' || typeof binding === 'string') {\n return record.binding === binding;\n }\n return (\n record.path === binding.path &&\n record.parse === binding.parse &&\n record.format === binding.format\n );\n }\n}\n",
|
|
27
|
+
"/** 跨端尺寸单位换算:rpx(微信 750 设计宽)在 H5 端转换为视口相对值。\n *\n * 1rpx = 视口宽度 / 750,且视口宽度上限 750px(桌面端不再放大,页面按\n * 750px 基准居中显示,与小程序设计宽一致)。\n */\n\n/** 根 CSS 变量:--tu-rpx 为 1rpx 的等效长度 */\nexport const ROOT_RPX_RULE =\n ':root{--tu-rpx:calc(min(100vw, 750px) / 750);}';\n\nconst RPX_PATTERN = /(-?\\d+(?:\\.\\d+)?)rpx/g;\n\n/** 把字符串/数值中的 `N rpx` 替换为 `calc(N * var(--tu-rpx))` */\nexport function convertRpx(value: string | number): string {\n return String(value).replace(\n RPX_PATTERN,\n (_match, number: string) => `calc(${number} * var(--tu-rpx))`\n );\n}\n",
|
|
28
|
+
"export interface ParsedEventName {\n eventName: string;\n modifiers: Set<string>;\n}\n\nimport { convertRpx } from '../../style/units';\n\nexport function isEventProp(key: string): boolean {\n return /^on[A-Z]/.test(key) || /^on[a-z]/.test(key);\n}\n\nexport function eventNameFromProp(key: string): string {\n return key.slice(2).toLowerCase();\n}\n\nexport function parseEventName(event: string): ParsedEventName {\n const [eventName, ...modifiers] = event.split('.');\n return { eventName, modifiers: new Set(modifiers) };\n}\n\nexport function wrapEventHandler(\n handler: (event: Event) => void,\n modifiers: Set<string>\n): EventListener {\n const eventHandler = (event: Event): void => {\n if (modifiers.has('stop')) {\n event.stopPropagation();\n }\n\n if (modifiers.has('prevent')) {\n event.preventDefault();\n }\n\n if (modifiers.has('self') && event.currentTarget !== event.target) {\n return;\n }\n\n if (modifiers.has('once')) {\n (event.currentTarget as EventTarget).removeEventListener(\n event.type,\n eventHandler\n );\n }\n\n handler(event);\n };\n\n return eventHandler;\n}\n\nexport function setStyleValue(\n style: CSSStyleDeclaration,\n property: string,\n value: string | number\n): void {\n const cssProperty = property.includes('-')\n ? property\n : property.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);\n\n style.setProperty(cssProperty, convertRpx(value));\n}\n",
|
|
29
|
+
"export interface StyleOptions {\n selector: string;\n properties: Record<string, string | number>;\n hover?: Record<string, string | number>;\n media?: Record<string, Record<string, string | number>>;\n}\n\nimport { convertRpx, ROOT_RPX_RULE } from './units';\n\nexport class StyleManager {\n public styleElement: HTMLStyleElement | null = null;\n public styles: Map<string, StyleOptions> = new Map();\n\n constructor() {\n // style 元素延迟到首次 addStyle 时创建,避免无样式组件产生空 <style> 节点\n }\n\n public addStyle(name: string, options: StyleOptions): void {\n this.styles.set(name, options);\n this.updateStyles();\n }\n\n public removeStyle(name: string): void {\n this.styles.delete(name);\n this.updateStyles();\n }\n\n public clearStyles(): void {\n this.styles.clear();\n if (this.styleElement) {\n this.styleElement.textContent = '';\n }\n }\n\n public destroy(): void {\n this.clearStyles();\n if (this.styleElement) {\n this.styleElement.remove();\n this.styleElement = null;\n }\n }\n\n private ensureStyleElement(): HTMLStyleElement {\n if (!this.styleElement) {\n const element = document.createElement('style');\n document.head.appendChild(element);\n this.styleElement = element;\n }\n return this.styleElement;\n }\n\n private convertToCSS(properties: Record<string, string | number>): string {\n return Object.entries(properties)\n .map(([key, value]) => {\n const cssKey = key.replace(/([A-Z])/g, '-$1').toLowerCase();\n return `${cssKey}: ${convertRpx(value)};`;\n })\n .join('\\n');\n }\n\n private updateStyles(): void {\n if (this.styles.size === 0) {\n if (this.styleElement) {\n this.styleElement.textContent = '';\n }\n return;\n }\n\n const element = this.ensureStyleElement();\n let cssText = ROOT_RPX_RULE + '\\n';\n\n this.styles.forEach((style) => {\n // 基础样式\n cssText += `${style.selector} {\\n${this.convertToCSS(style.properties)}\\n}\\n`;\n\n // hover 样式\n if (style.hover) {\n cssText += `${style.selector}:hover {\\n${this.convertToCSS(style.hover)}\\n}\\n`;\n }\n\n // media 查询\n if (style.media) {\n Object.entries(style.media).forEach(([query, properties]) => {\n cssText += `@media ${query} {\\n${style.selector} {\\n${this.convertToCSS(properties)}\\n}\\n}\\n`;\n });\n }\n });\n\n element.textContent = cssText;\n }\n}\n",
|
|
30
|
+
"import { effect, ReactiveEffect, stop } from './reactive';\n\nexport interface TemplateBinding {\n node: Text;\n originalText: string;\n effect: ReactiveEffect;\n}\n\nexport class TemplateEngine {\n private bindings: TemplateBinding[] = [];\n private readonly templateRegex = /{{(.*?)}}/g;\n\n constructor(public state: object) {\n if (!state || typeof state !== 'object') {\n throw new Error('TemplateEngine requires a valid state object');\n }\n }\n\n /**\n * 解析模板字符串,创建响应式的文本节点\n * @param text 包含模板表达式的文本字符串\n * @returns 创建的文本节点\n */\n public parseTemplate(text: string): Text {\n if (typeof text !== 'string') {\n text = String(text);\n }\n\n const textNode = document.createTextNode('');\n this.templateRegex.lastIndex = 0;\n const matches = Array.from(text.matchAll(this.templateRegex));\n\n if (matches && matches.length > 0) {\n this.setupReactiveBindings(textNode, text, matches);\n } else {\n // 如果没有模板表达式,直接设置文本内容\n textNode.textContent = text;\n }\n\n return textNode;\n }\n\n /**\n * 设置响应式绑定\n */\n private setupReactiveBindings(\n node: Text,\n originalText: string,\n matches: RegExpExecArray[]\n ): void {\n // 存储所有需要监控的键\n const keys = new Set<string>();\n\n // 第一次计算并设置文本内容\n const initialText = this.evaluateTemplate(originalText, matches, keys);\n node.textContent = initialText;\n\n // 创建响应式effect,当任何相关的状态变化时更新文本\n const effectFn = effect(() => {\n try {\n const updatedText = this.evaluateTemplate(originalText, matches, keys);\n if (node.textContent !== updatedText) {\n node.textContent = updatedText;\n }\n } catch (error) {\n console.error('Template update error:', error);\n node.textContent = `Error: ${error instanceof Error ? error.message : 'Unknown error'}`;\n }\n });\n\n // 存储绑定关系,以便后续清理\n this.bindings.push({\n node,\n originalText,\n effect: effectFn,\n });\n }\n\n /**\n * 计算模板文本的值\n */\n private evaluateTemplate(\n text: string,\n matches: RegExpExecArray[],\n keys: Set<string>\n ): string {\n let result = text;\n\n matches.forEach((match) => {\n const key = match[1]?.trim();\n if (key) {\n keys.add(key);\n\n // 尝试获取状态值\n const value = this.getValueFromState(key);\n // 将undefined或null转换为空字符串\n const displayValue =\n value === undefined || value === null ? '' : String(value);\n result = result.replace(match[0], displayValue);\n }\n });\n\n return result;\n }\n\n /**\n * 从状态对象中获取值,支持嵌套属性访问\n */\n private getValueFromState(keyPath: string): unknown {\n if (!keyPath) return undefined;\n\n // 支持嵌套属性访问,如 'user.name'\n const keys = keyPath.split('.');\n let value: unknown = this.state;\n\n for (const key of keys) {\n if (!value || typeof value !== 'object') {\n return undefined;\n }\n value = (value as Record<string, unknown>)[key];\n }\n\n return value;\n }\n\n /**\n * 清除所有模板绑定\n */\n public clearBindings(): void {\n // 停止所有effect\n this.bindings.forEach((binding) => {\n stop(binding.effect);\n });\n this.bindings = [];\n }\n\n /**\n * 获取当前的绑定数量\n */\n public getBindingCount(): number {\n return this.bindings.length;\n }\n\n /**\n * 检查模板是否包含表达式\n */\n public hasExpressions(text: string): boolean {\n this.templateRegex.lastIndex = 0;\n return this.templateRegex.test(text);\n }\n\n /**\n * 获取模板中的所有表达式键\n */\n public extractKeys(text: string): string[] {\n const keys: string[] = [];\n let match;\n const regex = new RegExp(this.templateRegex, 'g');\n\n while ((match = regex.exec(text)) !== null) {\n const key = match[1]?.trim();\n if (key) {\n keys.push(key);\n }\n }\n\n return keys;\n }\n\n /**\n * 计算模板值\n */\n public evaluateTemplateValue(text: string): string {\n this.templateRegex.lastIndex = 0;\n const matches = Array.from(text.matchAll(this.templateRegex));\n if (!matches || matches.length === 0) {\n return text;\n }\n\n const keys = new Set<string>();\n return this.evaluateTemplate(text, matches, keys);\n }\n}\n",
|
|
31
|
+
"import { StyleManager } from '../../style/StyleManager';\nimport {\n ReactiveEffect,\n ReactiveSystem,\n effect,\n reactive,\n reactiveScheduler,\n stop,\n} from '../reactive';\nimport {\n ComponentInstance,\n RenderRuntimeContext,\n RendererContext,\n} from '../renderer';\nimport { TemplateEngine } from '../template';\nimport { toChildren } from '../vnode';\nimport type { VNode } from '../vnode';\n\nexport type ComponentProps = object;\n\nexport type ComponentState = object;\n\nexport type ComponentEventListener = (...args: unknown[]) => void;\n\nexport type InjectionKey<T> = (string | symbol) & {\n readonly __injectionType?: T;\n};\n\nexport interface InjectionResult<T> {\n found: boolean;\n value: T | undefined;\n}\n\nexport type ComponentConstructor<\n TProps extends ComponentProps = ComponentProps,\n TState extends ComponentState = ComponentState,\n> = new (props?: TProps) => Component<TProps, TState>;\n\nexport type AnyComponentConstructor = new (\n props?: never\n) => Component<ComponentProps, ComponentState>;\n\nexport abstract class Component<\n TProps extends ComponentProps = ComponentProps,\n TState extends ComponentState = ComponentState,\n> implements ComponentInstance {\n private vnode: VNode | null = null;\n private el: Node | null = null;\n private readonly renderer = new RendererContext();\n private readonly templateEngine: TemplateEngine;\n private readonly childComponents = new Set<ComponentInstance>();\n private readonly eventListeners: Record<string, Set<ComponentEventListener>> =\n {};\n private readonly providers = new Map<string | symbol, unknown>();\n private readonly updateEffect: ReactiveEffect;\n private appContext: unknown = null;\n private parentComponent: ComponentInstance | null = null;\n private elementChangeListener:\n ((previousElement: Node, nextElement: Node) => void) | null = null;\n\n protected styleManager: StyleManager;\n public state: TState;\n public mounted = false;\n\n constructor(protected props: TProps = {} as TProps) {\n this.styleManager = new StyleManager();\n this.state = reactive(this.initState() ?? {});\n this.templateEngine = new TemplateEngine(this.state);\n this.initStyles();\n\n this.updateEffect = effect(\n () => {\n // 依赖在 render 期间按需收集(细粒度):只对 render 实际访问的\n // state 属性建立依赖,未访问的属性变化不会触发重渲染\n if (this.mounted) {\n try {\n this.update();\n } catch (error) {\n if (!this.dispatchError(error)) {\n throw error;\n }\n }\n }\n },\n {\n throwOnError: true,\n // 通过调度器合并同一批同步状态变更,避免逐次重渲染\n scheduler: (updateEffect) => {\n reactiveScheduler.enqueue(updateEffect);\n },\n }\n );\n }\n\n protected abstract initState(): TState;\n protected abstract initStyles(): void;\n protected abstract render(): VNode;\n\n public mount(container: HTMLElement): void {\n if (!container || !(container instanceof HTMLElement)) {\n throw new Error('Invalid container element');\n }\n\n try {\n container.appendChild(this.mountToNode());\n } catch (error) {\n console.error('组件渲染错误:', error);\n throw error;\n }\n }\n\n public mountToNode(): Node {\n if (this.mounted && this.el) {\n return this.el;\n }\n\n this.beforeMount();\n // 渲染与挂载在组件自身 effect 上下文中执行,避免子组件渲染\n // 把 state 依赖收集到父组件的 effect 上(跨组件依赖污染)。\n this.el = ReactiveSystem.getInstance().runWithEffect(\n this.updateEffect,\n () => {\n try {\n this.vnode = this.render();\n return this.renderer.mount(this.vnode, this.createRenderContext());\n } catch (error) {\n // 渲染或子组件挂载失败:先交给祖先错误边界,未处理才向外抛出\n if (this.dispatchError(error)) {\n return document.createComment('error-boundary');\n }\n throw error;\n }\n }\n );\n this.mounted = true;\n this.onMounted();\n return this.el;\n }\n\n public update(): void {\n if (!this.el || !this.vnode) {\n return;\n }\n\n this.beforeUpdate();\n const currentVNode = this.vnode;\n const currentElement = this.el;\n // 与 mountToNode 同理:render 期间的响应式访问只收集到自身 effect。\n this.el = ReactiveSystem.getInstance().runWithEffect(\n this.updateEffect,\n () => {\n const newVNode = this.render();\n const nextElement = this.renderer.patch(\n currentVNode,\n newVNode,\n currentElement,\n this.createRenderContext()\n );\n this.vnode = newVNode;\n return nextElement;\n }\n );\n if (currentElement !== this.el) {\n this.elementChangeListener?.(currentElement, this.el);\n }\n this.onUpdated();\n this.onPropsChange();\n }\n\n public unmount(): void {\n if (!this.mounted) {\n return;\n }\n\n this.beforeUnmount();\n\n if (this.vnode && this.el) {\n this.renderer.unmount(this.vnode, this.el, this.createRenderContext());\n }\n\n this.childComponents.clear();\n Object.keys(this.eventListeners).forEach((eventName) => {\n this.eventListeners[eventName].clear();\n delete this.eventListeners[eventName];\n });\n this.providers.clear();\n this.parentComponent = null;\n this.elementChangeListener = null;\n this.templateEngine.clearBindings();\n this.styleManager.destroy();\n stop(this.updateEffect);\n\n if (this.el?.parentNode) {\n this.el.parentNode.removeChild(this.el);\n }\n\n this.el = null;\n this.vnode = null;\n this.mounted = false;\n this.onUnmounted();\n }\n\n public setProps(props: Partial<TProps>): void {\n this.props = {\n ...this.props,\n ...props,\n };\n\n if (this.mounted) {\n // 与 state 更新走同一调度器:同一批多次 setProps 合并为一次更新\n reactiveScheduler.enqueue(this.updateEffect);\n }\n }\n\n public setState(state: Partial<TState>): void {\n Object.assign(this.state, state);\n }\n\n public setAppContext(context: unknown): void {\n this.appContext = context;\n this.childComponents.forEach((child) => {\n child.setAppContext?.(context);\n });\n }\n\n public setParentComponent(parent: ComponentInstance | null): void {\n this.parentComponent = parent;\n }\n\n public getParentComponent(): ComponentInstance | null {\n return this.parentComponent;\n }\n\n public setElementChangeListener(\n listener: (previousElement: Node, nextElement: Node) => void\n ): void {\n this.elementChangeListener = listener;\n }\n\n public provide<T>(key: InjectionKey<T>, value: T): void {\n this.providers.set(key, value);\n }\n\n public inject<T>(key: InjectionKey<T>): T | undefined;\n public inject<T>(key: InjectionKey<T>, fallback: T): T;\n public inject<T>(key: InjectionKey<T>, fallback?: T): T | undefined {\n const result = this.resolveInjection(key);\n return result.found ? result.value : fallback;\n }\n\n public resolveInjection<T>(key: InjectionKey<T>): InjectionResult<T> {\n if (this.providers.has(key)) {\n return { found: true, value: this.providers.get(key) as T | undefined };\n }\n\n if (this.parentComponent?.resolveInjection) {\n return this.parentComponent.resolveInjection(key);\n }\n\n return this.resolveAppInjection(key);\n }\n\n public getElement(): Node | null {\n return this.el;\n }\n\n protected beforeMount(): void {}\n\n protected onMounted(): void {}\n\n protected beforeUpdate(): void {}\n\n protected onUpdated(): void {}\n /** props 更新钩子:Web 端在每次更新(setProps/setState)后调用;小程序端由 observers 在 properties 变化时触发。 */\n protected onPropsChange(): void {}\n\n protected beforeUnmount(): void {}\n\n protected onUnmounted(): void {}\n\n /**\n * 错误边界钩子:后代组件渲染或更新抛错时被调用。\n * 返回 false 表示错误已被处理并停止继续向上传播;\n * 返回 true 或 undefined 时错误继续冒泡到更外层边界。\n */\n protected onErrorCaptured?(\n error: unknown,\n instance: ComponentInstance\n ): boolean | void;\n\n /**\n * 沿组件父链寻找最近的错误边界。返回 true 表示错误已被边界处理。\n */\n private dispatchError(error: unknown): boolean {\n let current: ComponentInstance | null = this.getParentComponent();\n while (current) {\n // 同类实例可访问 protected 生命周期钩子\n const boundary = current as unknown as Component;\n if (typeof boundary.onErrorCaptured === 'function') {\n const handled = boundary.onErrorCaptured(error, this);\n if (handled === false) {\n return true;\n }\n }\n current = current.getParentComponent?.() ?? null;\n }\n return false;\n }\n\n protected getContext(): unknown {\n return this.appContext;\n }\n\n protected get router() {\n return this.getRouterFrom(this.appContext) ?? this.getRouterFromGlobalApp();\n }\n\n protected emit(eventName: string, ...args: unknown[]): void {\n this.eventListeners[eventName]?.forEach((listener) => {\n listener(...args);\n });\n }\n\n public on(eventName: string, listener: ComponentEventListener): () => void {\n if (!this.eventListeners[eventName]) {\n this.eventListeners[eventName] = new Set();\n }\n this.eventListeners[eventName].add(listener);\n\n return () => this.off(eventName, listener);\n }\n\n public off(eventName: string, listener: ComponentEventListener): void {\n this.eventListeners[eventName]?.delete(listener);\n }\n\n private createRenderContext(): RenderRuntimeContext {\n return {\n appContext: this.appContext,\n templateEngine: this.templateEngine,\n renderer: this.renderer,\n slots: this.collectSlots(),\n registerChild: (component) => {\n this.childComponents.add(component);\n component.setParentComponent?.(this);\n component.setAppContext?.(this.appContext);\n },\n unregisterChild: (component) => {\n this.childComponents.delete(component);\n component.setParentComponent?.(null);\n },\n };\n }\n\n private collectSlots(): Record<string, Array<VNode | string>> {\n const slots: Record<string, Array<VNode | string>> = { default: [] };\n const children = toChildren(\n (this.props as { children?: Array<VNode | string> }).children\n );\n\n children.forEach((child) => {\n const slotName = this.getSlotName(child);\n if (!slots[slotName]) {\n slots[slotName] = [];\n }\n slots[slotName].push(this.normalizeSlotChild(child));\n });\n\n return slots;\n }\n\n private getSlotName(child: VNode | string): string {\n if (typeof child === 'string') {\n return 'default';\n }\n\n return 'slot' in child && typeof child.slot === 'string'\n ? child.slot\n : 'default';\n }\n\n private normalizeSlotChild(child: VNode | string): VNode | string {\n if (typeof child === 'string' || !('slot' in child)) {\n return child;\n }\n\n const clone = { ...child };\n delete clone.slot;\n return clone as VNode;\n }\n\n private getRouterFrom(value: unknown): unknown {\n if (!value || typeof value !== 'object' || !('router' in value)) {\n return undefined;\n }\n\n return (value as { router?: unknown }).router;\n }\n\n private getRouterFromGlobalApp(): unknown {\n const globalApp = (globalThis as { __APP__?: unknown }).__APP__;\n return this.getRouterFrom(globalApp);\n }\n\n private resolveAppInjection<T>(key: InjectionKey<T>): InjectionResult<T> {\n if (!this.appContext || typeof this.appContext !== 'object') {\n return { found: false, value: undefined };\n }\n\n const app = (\n this.appContext as {\n app?: {\n resolveInjection?: (\n injectionKey: InjectionKey<T>\n ) => InjectionResult<T>;\n };\n }\n ).app;\n\n return app?.resolveInjection?.(key) ?? { found: false, value: undefined };\n }\n}\n",
|
|
32
|
+
"import { Router } from './index';\n\n// 创建一个全局路由实例\nlet router: Router | null = null;\n\n/**\n * 设置全局路由实例\n * @param r 路由实例\n */\nexport function setRouter(r: Router): void {\n if (!r || !(r instanceof Router)) {\n throw new Error('Invalid router instance');\n }\n router = r;\n}\n\n/**\n * 获取路由实例\n * @returns 路由实例\n * @throws 当路由未初始化时抛出错误\n */\nexport function useRouter(): Router {\n if (!router) {\n throw new Error(\n 'Router is not initialized. Please make sure you have installed the router plugin.'\n );\n }\n return router;\n}\n\n/**\n * 安全地获取路由实例,如果未初始化则返回null\n * @returns 路由实例或null\n */\nexport function getRouter(): Router | null {\n return router;\n}\n\n/**\n * 检查路由是否已初始化\n * @returns 是否已初始化\n */\nexport function hasRouter(): boolean {\n return router !== null;\n}\n\n/**\n * 重置路由实例(主要用于测试)\n */\nexport function resetRouter(): void {\n router = null;\n}\n",
|
|
33
|
+
"import { ModelBindingController } from '../model';\nimport { effect, ReactiveEffect, stop } from '../reactive';\nimport {\n EventListeners,\n HTMLNode,\n HTMLProps,\n VNode,\n isHTMLNode,\n toChildren,\n} from '../vnode';\nimport {\n eventNameFromProp,\n isEventProp,\n parseEventName,\n setStyleValue,\n wrapEventHandler,\n} from './props';\nimport type { RenderRuntimeContext, RenderStrategy, Renderable } from './types';\nimport { getRouter } from '../../router/instance';\n\nexport const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';\n\n/**\n * 仅包含 HTML 中不存在的 SVG 专属标签:这些标签必须通过\n * createElementNS 创建,否则会落入 HTML 命名空间而无法渲染。\n * 与 HTML 重名的标签(title/desc/a/image 等)不在此列,避免误伤。\n */\nexport const SVG_TAGS: ReadonlySet<string> = new Set([\n 'svg',\n 'g',\n 'defs',\n 'rect',\n 'circle',\n 'ellipse',\n 'line',\n 'polyline',\n 'polygon',\n 'path',\n 'text',\n 'tspan',\n 'textPath',\n 'use',\n 'symbol',\n 'marker',\n 'linearGradient',\n 'radialGradient',\n 'stop',\n 'pattern',\n 'mask',\n 'clipPath',\n 'foreignObject',\n]);\n\nexport class ElementRenderStrategy<\n TNode extends HTMLNode = HTMLNode,\n> implements RenderStrategy<TNode> {\n private readonly listeners = new WeakMap<\n Element,\n Map<string, { eventName: string; listener: EventListener }>\n >();\n private readonly effects = new WeakMap<Element, Set<ReactiveEffect>>();\n private readonly modelBindings = new ModelBindingController();\n\n public matches(vnode: Renderable): vnode is TNode {\n return typeof vnode === 'object' && vnode !== null && isHTMLNode(vnode);\n }\n\n public mount(vnode: TNode, context: RenderRuntimeContext): Node {\n if (vnode.directions?.if === false) {\n return document.createComment('if');\n }\n\n // H5 端 navigator(小程序原生组件):渲染为 <a> 并接管点击走 SPA 路由;\n // url / openType 为小程序导航语义,不落到 DOM 属性上。\n const isNavigator = vnode.tag === 'navigator';\n const element = isNavigator\n ? document.createElement('a')\n : SVG_TAGS.has(vnode.tag)\n ? document.createElementNS(SVG_NAMESPACE, vnode.tag)\n : document.createElement(vnode.tag);\n\n const props = vnode.props ?? {};\n if (isNavigator) {\n const { url: targetUrl, openType: _openType, ...restProps } = props;\n const url = typeof targetUrl === 'string' ? targetUrl : '#';\n element.setAttribute('href', url);\n element.addEventListener('click', (event) => {\n const router = getRouter();\n if (router) {\n event.preventDefault();\n void router.push(element.getAttribute('href') ?? '#');\n }\n });\n this.applyProps(element, {}, restProps, context);\n } else {\n this.applyProps(element, {}, props, context);\n }\n this.updateListeners(element, {}, this.collectListeners(vnode));\n this.mountChildren(element, vnode, context);\n this.applyDirections(element, undefined, vnode.directions, context);\n\n return element;\n }\n\n public patch(\n oldVNode: TNode,\n newVNode: TNode,\n currentNode: Node,\n context: RenderRuntimeContext\n ): Node {\n if (\n oldVNode.tag !== newVNode.tag ||\n currentNode.nodeType === Node.COMMENT_NODE\n ) {\n const nextNode = this.mount(newVNode, context);\n currentNode.parentNode?.replaceChild(nextNode, currentNode);\n this.unmount(oldVNode, currentNode, context);\n return nextNode;\n }\n\n // SVG 元素是 SVGElement 而非 HTMLElement,元素级补丁必须覆盖两者\n if (!(currentNode instanceof Element)) {\n return currentNode;\n }\n\n if (newVNode.directions?.if === false) {\n const nextNode = document.createComment('if');\n currentNode.parentNode?.replaceChild(nextNode, currentNode);\n this.unmount(oldVNode, currentNode, context);\n return nextNode;\n }\n\n const isNavigator =\n newVNode.tag === 'navigator' || oldVNode.tag === 'navigator';\n if (isNavigator) {\n const oldProps = oldVNode.props ?? {};\n const newProps = newVNode.props ?? {};\n const oldUrl = typeof oldProps.url === 'string' ? oldProps.url : '';\n const newUrl = typeof newProps.url === 'string' ? newProps.url : '';\n if (oldUrl !== newUrl) {\n currentNode.setAttribute('href', newUrl);\n }\n const newNav = { ...newProps };\n delete (newNav as { url?: unknown }).url;\n delete (newNav as { openType?: unknown }).openType;\n this.applyProps(currentNode, oldProps, newNav, context);\n } else {\n this.applyProps(\n currentNode,\n oldVNode.props ?? {},\n newVNode.props ?? {},\n context\n );\n }\n this.updateListeners(\n currentNode,\n this.collectListeners(oldVNode),\n this.collectListeners(newVNode)\n );\n this.updateChildren(currentNode, oldVNode, newVNode, context);\n this.applyDirections(\n currentNode,\n oldVNode.directions,\n newVNode.directions,\n context\n );\n\n return currentNode;\n }\n\n public unmount(\n vnode: TNode,\n currentNode: Node,\n context: RenderRuntimeContext\n ): void {\n if (!(currentNode instanceof Element)) {\n return;\n }\n\n this.effects.get(currentNode)?.forEach((item) => stop(item));\n this.effects.delete(currentNode);\n\n this.listeners.get(currentNode)?.forEach(({ eventName, listener }) => {\n currentNode.removeEventListener(eventName, listener);\n });\n this.listeners.delete(currentNode);\n this.modelBindings.cleanup(currentNode as HTMLElement);\n this.unmountChildren(currentNode, vnode, context);\n }\n\n protected mountChildren(\n element: Element,\n vnode: TNode,\n context: RenderRuntimeContext\n ): void {\n toChildren(vnode.children).forEach((child) => {\n element.appendChild(context.renderer.mount(child, context));\n });\n }\n\n protected updateChildren(\n element: Element,\n oldVNode: TNode,\n newVNode: TNode,\n context: RenderRuntimeContext\n ): void {\n this.updateOrdinaryChildren(\n element,\n toChildren(oldVNode.children),\n toChildren(newVNode.children),\n context\n );\n }\n\n protected unmountChildren(\n element: Element,\n vnode: TNode,\n context: RenderRuntimeContext\n ): void {\n toChildren(vnode.children).forEach((child, index) => {\n const childNode = element.childNodes[index];\n if (childNode) {\n context.renderer.unmount(child, childNode, context);\n }\n });\n }\n\n private applyProps(\n element: Element,\n oldProps: HTMLProps,\n newProps: HTMLProps,\n context: RenderRuntimeContext\n ): void {\n Object.keys(oldProps).forEach((key) => {\n if (isEventProp(key) || key in newProps) {\n return;\n }\n\n if (key === 'className' || key === 'class') {\n element.removeAttribute('class');\n } else if (key === 'style') {\n element.removeAttribute('style');\n } else if (\n key === 'value' &&\n typeof element === 'object' &&\n element !== null &&\n 'value' in element\n ) {\n (element as HTMLElement & { value: string }).value = '';\n } else {\n element.removeAttribute(attributeName(key));\n }\n });\n\n Object.entries(newProps).forEach(([key, value]) => {\n if (isEventProp(key)) {\n return;\n }\n\n // 值未变化时跳过 DOM 更新,避免每次 patch 全量重写属性\n if (oldProps[key] === value) {\n return;\n }\n\n if (key === 'className' || key === 'class') {\n // SVG 元素的 className 是 SVGAnimatedString,直接赋值在部分环境\n // 不可靠;统一走 class 属性。\n if (element.namespaceURI === SVG_NAMESPACE) {\n element.setAttribute('class', String(value ?? ''));\n } else {\n (element as HTMLElement).className = String(value ?? '');\n }\n return;\n }\n\n // 表单控件(input/textarea/select/option)的 value 必须写入 DOM\n // property 而不是 attribute:setAttribute('value') 只更新默认值,\n // 用户输入或程序修改后的当前值不会随之变化。\n if (\n key === 'value' &&\n typeof element === 'object' &&\n element !== null &&\n 'value' in element\n ) {\n (element as HTMLElement & { value: string }).value = String(\n value ?? ''\n );\n return;\n }\n\n if (key === 'style' && typeof value === 'object' && value !== null) {\n element.removeAttribute('style');\n const style = (element as HTMLElement).style;\n Object.entries(value).forEach(([cssKey, cssValue]) => {\n setStyleValue(style, cssKey, cssValue);\n });\n return;\n }\n\n if (value === false || value === undefined || value === null) {\n element.removeAttribute(attributeName(key));\n return;\n }\n\n if (value === true) {\n element.setAttribute(attributeName(key), '');\n return;\n }\n\n if (\n typeof value === 'string' &&\n context.templateEngine.hasExpressions(value)\n ) {\n this.setupReactiveAttribute(element, key, value, context);\n return;\n }\n\n element.setAttribute(attributeName(key), String(value));\n });\n }\n\n private applyDirections(\n element: Element,\n oldDirections: HTMLNode['directions'],\n newDirections: HTMLNode['directions'],\n context: RenderRuntimeContext\n ): void {\n if (newDirections && 'show' in newDirections) {\n (element as HTMLElement).style.display = newDirections.show ? '' : 'none';\n } else if (oldDirections && 'show' in oldDirections) {\n (element as HTMLElement).style.display = '';\n }\n\n if (!newDirections?.model) {\n this.modelBindings.cleanup(element as HTMLElement);\n return;\n }\n\n this.modelBindings.bind(\n element as HTMLElement,\n newDirections.model,\n context.templateEngine.state as Record<string, unknown>\n );\n }\n\n private updateOrdinaryChildren(\n element: Element,\n oldChildren: Array<VNode | string>,\n newChildren: Array<VNode | string>,\n context: RenderRuntimeContext\n ): void {\n this.assertNoDuplicateKeys(oldChildren);\n this.assertNoDuplicateKeys(newChildren);\n\n if (this.hasOnlyKeyedChildren(oldChildren, newChildren)) {\n this.updateKeyedChildren(element, oldChildren, newChildren, context);\n return;\n }\n\n const sharedLength = Math.min(oldChildren.length, newChildren.length);\n\n for (let index = 0; index < sharedLength; index += 1) {\n const childNode = element.childNodes[index];\n if (!childNode) {\n element.appendChild(\n context.renderer.mount(newChildren[index], context)\n );\n continue;\n }\n context.renderer.patch(\n oldChildren[index],\n newChildren[index],\n childNode,\n context\n );\n }\n\n for (let index = sharedLength; index < newChildren.length; index += 1) {\n element.appendChild(context.renderer.mount(newChildren[index], context));\n }\n\n for (\n let index = oldChildren.length - 1;\n index >= newChildren.length;\n index -= 1\n ) {\n const childNode = element.childNodes[index];\n if (childNode) {\n context.renderer.unmount(oldChildren[index], childNode, context);\n if (childNode.parentNode === element) {\n element.removeChild(childNode);\n }\n }\n }\n }\n\n private updateKeyedChildren(\n element: Element,\n oldChildren: Array<VNode | string>,\n newChildren: Array<VNode | string>,\n context: RenderRuntimeContext\n ): void {\n const oldEntries = oldChildren.map((vnode, index) => ({\n vnode,\n node: element.childNodes[index],\n index,\n }));\n const keyedOldEntries = new Map<\n string | number,\n { vnode: VNode | string; node: Node; index: number }\n >();\n const usedOldIndexes = new Set<number>();\n\n oldEntries.forEach((entry) => {\n const key = this.getVNodeKey(entry.vnode);\n if (key !== undefined && entry.node) {\n keyedOldEntries.set(key, {\n vnode: entry.vnode,\n node: entry.node,\n index: entry.index,\n });\n }\n });\n\n newChildren.forEach((newChild, newIndex) => {\n const key = this.getVNodeKey(newChild);\n const oldEntry = key === undefined ? undefined : keyedOldEntries.get(key);\n let nextNode: Node;\n\n if (oldEntry) {\n nextNode = context.renderer.patch(\n oldEntry.vnode,\n newChild,\n oldEntry.node,\n context\n );\n usedOldIndexes.add(oldEntry.index);\n } else {\n nextNode = context.renderer.mount(newChild, context);\n }\n\n const referenceNode = element.childNodes[newIndex] ?? null;\n if (nextNode !== referenceNode) {\n element.insertBefore(nextNode, referenceNode);\n }\n });\n\n oldEntries.forEach((entry) => {\n if (!entry.node || usedOldIndexes.has(entry.index)) {\n return;\n }\n\n context.renderer.unmount(entry.vnode, entry.node, context);\n if (entry.node.parentNode === element) {\n element.removeChild(entry.node);\n }\n });\n }\n\n private hasOnlyKeyedChildren(\n oldChildren: Array<VNode | string>,\n newChildren: Array<VNode | string>\n ): boolean {\n return [...oldChildren, ...newChildren].every(\n (child) => this.getVNodeKey(child) !== undefined\n );\n }\n\n private assertNoDuplicateKeys(children: Array<VNode | string>): void {\n const keys = new Set<string | number>();\n\n children.forEach((child) => {\n const key = this.getVNodeKey(child);\n if (key === undefined) {\n return;\n }\n\n if (keys.has(key)) {\n throw new Error(`Duplicate key \"${key}\"`);\n }\n\n keys.add(key);\n });\n }\n\n private getVNodeKey(vnode: VNode | string): string | number | undefined {\n if (typeof vnode === 'string') {\n return undefined;\n }\n\n return vnode.key;\n }\n\n private collectListeners(vnode: HTMLNode): EventListeners {\n const listeners: EventListeners = {};\n\n Object.entries(vnode.props ?? {}).forEach(([key, value]) => {\n if (isEventProp(key) && typeof value === 'function') {\n listeners[eventNameFromProp(key)] = value as (event: Event) => void;\n }\n });\n\n return {\n ...listeners,\n ...(vnode.listeners ?? {}),\n };\n }\n\n private updateListeners(\n element: Element,\n oldListeners: EventListeners,\n newListeners: EventListeners\n ): void {\n const store = this.listeners.get(element) ?? new Map();\n const oldKeys = new Set(Object.keys(oldListeners));\n const newKeys = new Set(Object.keys(newListeners));\n\n oldKeys.forEach((event) => {\n if (!newKeys.has(event) || oldListeners[event] !== newListeners[event]) {\n const stored = store.get(event);\n if (stored) {\n element.removeEventListener(stored.eventName, stored.listener);\n store.delete(event);\n }\n }\n });\n\n newKeys.forEach((event) => {\n if (!oldKeys.has(event) || oldListeners[event] !== newListeners[event]) {\n const { eventName, modifiers } = parseEventName(event);\n // Web 没有原生 confirm 事件:映射为 keydown + Enter 回车确认,\n // 与小程序 input 的 bindconfirm 语义一致(textarea 保留换行,不映射)。\n const isTextarea =\n typeof element.tagName === 'string' &&\n element.tagName.toLowerCase() === 'textarea';\n const domEvent = eventName === 'confirm' && !isTextarea ? 'keydown' : eventName;\n const listener = wrapEventHandler(\n (domEventArg: Event) => {\n if (domEvent === 'keydown') {\n const key = (domEventArg as KeyboardEvent).key;\n if (key !== 'Enter') {\n return;\n }\n }\n newListeners[event](domEventArg);\n },\n modifiers\n );\n element.addEventListener(domEvent, listener);\n store.set(event, { eventName: domEvent, listener });\n }\n });\n\n this.listeners.set(element, store);\n }\n\n private setupReactiveAttribute(\n element: Element,\n attrName: string,\n attrValue: string,\n context: RenderRuntimeContext\n ): void {\n const effectRef = effect(() => {\n element.setAttribute(\n attrName,\n context.templateEngine.evaluateTemplateValue(attrValue)\n );\n });\n\n this.trackEffect(element, effectRef);\n }\n\n private trackEffect(element: Element, effectRef: ReactiveEffect): void {\n const effects = this.effects.get(element) ?? new Set();\n effects.add(effectRef);\n this.effects.set(element, effects);\n }\n}\n\n/** dataId -> data-id:元素属性写入 data-id,事件回调可从 currentTarget.dataset 读取。 */\nfunction attributeName(key: string): string {\n if (key.startsWith('data') && key.length > 4) {\n return `data${key\n .slice(4)\n .replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`)}`;\n }\n // 标准 HTML 属性名不使用 camelCase\n if (key === 'tabIndex' || key === 'colSpan' || key === 'htmlFor') {\n return key.toLowerCase();\n }\n return key;\n}\n",
|
|
34
|
+
"import type { ComponentEventListener, ComponentProps } from './component/base';\nimport {\n ComponentNode,\n SlotProvider,\n VNode,\n isComponentNode,\n isSlotProvider,\n toChildren,\n} from './vnode';\nimport { ElementRenderStrategy } from './renderer/element-strategy';\nexport { ElementRenderStrategy } from './renderer/element-strategy';\nexport type {\n ComponentInstance,\n RenderRuntimeContext,\n RenderStrategy,\n Renderable,\n RendererHost,\n} from './renderer/types';\nimport type {\n ComponentInstance,\n RenderRuntimeContext,\n RenderStrategy,\n Renderable,\n} from './renderer/types';\n\nexport class RendererContext {\n private readonly strategies: RenderStrategy[];\n\n constructor() {\n this.strategies = [\n new TextRenderStrategy(),\n new ComponentRenderStrategy(),\n new SlotRenderStrategy(),\n new ElementRenderStrategy(),\n ];\n }\n\n public mount(vnode: Renderable, context: RenderRuntimeContext): Node {\n return this.findStrategy(vnode).mount(vnode as never, context);\n }\n\n public patch(\n oldVNode: Renderable,\n newVNode: Renderable,\n currentNode: Node,\n context: RenderRuntimeContext\n ): Node {\n const oldStrategy = this.findStrategy(oldVNode);\n const newStrategy = this.findStrategy(newVNode);\n\n if (oldStrategy !== newStrategy) {\n const nextNode = newStrategy.mount(newVNode as never, context);\n currentNode.parentNode?.replaceChild(nextNode, currentNode);\n oldStrategy.unmount(oldVNode as never, currentNode, context);\n return nextNode;\n }\n\n return oldStrategy.patch(\n oldVNode as never,\n newVNode as never,\n currentNode,\n context\n );\n }\n\n public unmount(\n vnode: Renderable,\n currentNode: Node,\n context: RenderRuntimeContext\n ): void {\n this.findStrategy(vnode).unmount(vnode as never, currentNode, context);\n }\n\n private findStrategy(vnode: Renderable): RenderStrategy {\n const strategy = this.strategies.find((item) => item.matches(vnode));\n if (!strategy) {\n throw new Error('No render strategy found for vnode');\n }\n return strategy;\n }\n}\n\nexport class TextRenderStrategy implements RenderStrategy<string> {\n public matches(vnode: Renderable): vnode is string {\n return typeof vnode === 'string';\n }\n\n public mount(vnode: string, context: RenderRuntimeContext): Node {\n return context.templateEngine.parseTemplate(vnode);\n }\n\n public patch(\n oldVNode: string,\n newVNode: string,\n currentNode: Node,\n context: RenderRuntimeContext\n ): Node {\n if (oldVNode === newVNode) {\n return currentNode;\n }\n\n const nextNode = this.mount(newVNode, context);\n currentNode.parentNode?.replaceChild(nextNode, currentNode);\n return nextNode;\n }\n\n public unmount(): void {}\n}\n\nexport class ComponentRenderStrategy implements RenderStrategy<ComponentNode> {\n private readonly instances = new WeakMap<Node, ComponentInstance>();\n private readonly instanceNodes = new Map<ComponentInstance, Set<Node>>();\n private readonly emitterUnsubscribers = new WeakMap<\n ComponentInstance,\n Map<string, { listener: ComponentEventListener; unsubscribe: () => void }>\n >();\n\n public matches(vnode: Renderable): vnode is ComponentNode {\n return (\n typeof vnode === 'object' && vnode !== null && isComponentNode(vnode)\n );\n }\n\n public mount(vnode: ComponentNode, context: RenderRuntimeContext): Node {\n if (vnode.directions?.if === false) {\n return document.createComment('if');\n }\n\n const ComponentClass = vnode.component as new (\n props?: ComponentProps\n ) => ComponentInstance;\n const instance = new ComponentClass(this.createProps(vnode));\n\n if (context.appContext && instance.setAppContext) {\n instance.setAppContext(context.appContext);\n }\n\n this.syncEmitters(instance, vnode.emitters ?? {});\n\n context.registerChild(instance);\n\n const node = instance.mountToNode();\n this.trackInstanceNode(instance, node);\n instance.setElementChangeListener?.((previousNode, nextNode) => {\n this.trackInstanceNode(instance, previousNode);\n this.trackInstanceNode(instance, nextNode);\n });\n return node;\n }\n\n public patch(\n oldVNode: ComponentNode,\n newVNode: ComponentNode,\n currentNode: Node,\n context: RenderRuntimeContext\n ): Node {\n if (currentNode.nodeType === Node.COMMENT_NODE) {\n const nextNode = this.mount(newVNode, context);\n currentNode.parentNode?.replaceChild(nextNode, currentNode);\n return nextNode;\n }\n\n if (newVNode.directions?.if === false) {\n const nextNode = document.createComment('if');\n currentNode.parentNode?.replaceChild(nextNode, currentNode);\n this.unmount(oldVNode, currentNode, context);\n return nextNode;\n }\n\n const instance = this.instances.get(currentNode);\n\n if (instance && oldVNode.component === newVNode.component) {\n this.syncEmitters(instance, newVNode.emitters ?? {});\n instance.setProps(this.createProps(newVNode));\n const nextNode = instance.getElement() ?? currentNode;\n this.trackInstanceNode(instance, nextNode);\n return nextNode;\n }\n\n const nextNode = this.mount(newVNode, context);\n currentNode.parentNode?.replaceChild(nextNode, currentNode);\n this.unmount(oldVNode, currentNode, context);\n return nextNode;\n }\n\n public unmount(\n _vnode: ComponentNode,\n currentNode: Node,\n context: RenderRuntimeContext\n ): void {\n const instance = this.instances.get(currentNode);\n if (instance) {\n this.clearEmitters(instance);\n instance.unmount();\n this.clearInstanceNodes(instance);\n context.unregisterChild(instance);\n }\n }\n\n private createProps(vnode: ComponentNode): ComponentProps {\n return {\n ...(vnode.props ?? {}),\n children: toChildren(vnode.children),\n };\n }\n\n private syncEmitters(\n instance: ComponentInstance,\n emitters: NonNullable<ComponentNode['emitters']>\n ): void {\n const current = this.emitterUnsubscribers.get(instance) ?? new Map();\n\n current.forEach(({ listener: currentListener, unsubscribe }, eventName) => {\n const listener = emitters[eventName];\n if (!listener || listener !== currentListener) {\n unsubscribe();\n current.delete(eventName);\n }\n });\n\n Object.entries(emitters).forEach(([eventName, listener]) => {\n if (current.get(eventName)?.listener === listener) {\n return;\n }\n\n current.set(eventName, {\n listener,\n unsubscribe: instance.on(eventName, listener),\n });\n });\n\n this.emitterUnsubscribers.set(instance, current);\n }\n\n private clearEmitters(instance: ComponentInstance): void {\n this.emitterUnsubscribers.get(instance)?.forEach(({ unsubscribe }) => {\n unsubscribe();\n });\n this.emitterUnsubscribers.delete(instance);\n }\n\n private trackInstanceNode(instance: ComponentInstance, node: Node): void {\n this.instances.set(node, instance);\n const nodes = this.instanceNodes.get(instance) ?? new Set<Node>();\n nodes.add(node);\n this.instanceNodes.set(instance, nodes);\n }\n\n private clearInstanceNodes(instance: ComponentInstance): void {\n this.instanceNodes.get(instance)?.forEach((node) => {\n this.instances.delete(node);\n });\n this.instanceNodes.delete(instance);\n }\n}\n\nexport class SlotRenderStrategy implements RenderStrategy<SlotProvider> {\n private readonly renderedChildren = new WeakMap<\n HTMLElement,\n Array<VNode | string>\n >();\n\n public matches(vnode: Renderable): vnode is SlotProvider {\n return typeof vnode === 'object' && vnode !== null && isSlotProvider(vnode);\n }\n\n public mount(vnode: SlotProvider, context: RenderRuntimeContext): Node {\n if (vnode.directions?.if === false) {\n return document.createComment('if');\n }\n\n const slotContainer = document.createElement('div');\n slotContainer.setAttribute('data-slot', vnode.props.name);\n this.mountSlotChildren(\n slotContainer,\n this.resolveChildren(vnode, context),\n context\n );\n return slotContainer;\n }\n\n public patch(\n oldVNode: SlotProvider,\n newVNode: SlotProvider,\n currentNode: Node,\n context: RenderRuntimeContext\n ): Node {\n if (currentNode.nodeType === Node.COMMENT_NODE) {\n const nextNode = this.mount(newVNode, context);\n currentNode.parentNode?.replaceChild(nextNode, currentNode);\n return nextNode;\n }\n\n if (newVNode.directions?.if === false) {\n const nextNode = document.createComment('if');\n currentNode.parentNode?.replaceChild(nextNode, currentNode);\n this.unmount(oldVNode, currentNode, context);\n return nextNode;\n }\n\n if (currentNode instanceof HTMLElement) {\n currentNode.setAttribute('data-slot', newVNode.props.name);\n this.replaceSlotChildren(currentNode, oldVNode, newVNode, context);\n }\n return currentNode;\n }\n\n public unmount(\n _vnode: SlotProvider,\n currentNode: Node,\n context: RenderRuntimeContext\n ): void {\n if (!(currentNode instanceof HTMLElement)) {\n return;\n }\n\n this.unmountSlotChildren(currentNode, context);\n this.renderedChildren.delete(currentNode);\n }\n\n private resolveChildren(\n vnode: SlotProvider,\n context: RenderRuntimeContext\n ): Array<VNode | string> {\n return context.slots[vnode.props.name] ?? toChildren(vnode.children);\n }\n\n private replaceSlotChildren(\n element: HTMLElement,\n _oldVNode: SlotProvider,\n newVNode: SlotProvider,\n context: RenderRuntimeContext\n ): void {\n const oldChildren = this.renderedChildren.get(element) ?? [];\n const newChildren = this.resolveChildren(newVNode, context);\n const sharedLength = Math.min(oldChildren.length, newChildren.length);\n\n // 复用已有节点逐个 patch,避免 slot 内容整体重建导致\n // 组件实例与 DOM 元素(含焦点)丢失。\n for (let index = 0; index < sharedLength; index += 1) {\n const childNode = element.childNodes[index];\n if (!childNode) {\n element.appendChild(\n context.renderer.mount(newChildren[index], context)\n );\n continue;\n }\n context.renderer.patch(\n oldChildren[index],\n newChildren[index],\n childNode,\n context\n );\n }\n\n for (let index = sharedLength; index < newChildren.length; index += 1) {\n element.appendChild(context.renderer.mount(newChildren[index], context));\n }\n\n for (\n let index = oldChildren.length - 1;\n index >= newChildren.length;\n index -= 1\n ) {\n const childNode = element.childNodes[index];\n if (childNode) {\n context.renderer.unmount(oldChildren[index], childNode, context);\n if (childNode.parentNode === element) {\n element.removeChild(childNode);\n }\n }\n }\n\n this.renderedChildren.set(element, newChildren);\n }\n\n private mountSlotChildren(\n element: HTMLElement,\n children: Array<VNode | string>,\n context: RenderRuntimeContext\n ): void {\n children.forEach((child) => {\n element.appendChild(context.renderer.mount(child, context));\n });\n this.renderedChildren.set(element, children);\n }\n\n private unmountSlotChildren(\n element: HTMLElement,\n context: RenderRuntimeContext\n ): void {\n const children = this.renderedChildren.get(element) ?? [];\n children.forEach((child, index) => {\n const childNode = element.childNodes[index];\n if (childNode) {\n context.renderer.unmount(child, childNode, context);\n }\n });\n }\n}\n",
|
|
35
|
+
"import type { RequestConfig, RequestResponse } from './types';\n\n/**\n * 统一请求错误码:\n * - NETWORK_ERROR:网络层失败(断网、DNS、连接被拒等)\n * - TIMEOUT:超时\n * - ABORTED:被 AbortSignal 取消\n * - BAD_REQUEST:配置非法(缺少 url、方法不支持等)\n * - UNSUPPORTED_PLATFORM:当前环境没有可用的请求 API,或指定了不支持的端\n * - UNKNOWN:无法归类的失败\n */\nexport type RequestErrorCode =\n | 'NETWORK_ERROR'\n | 'TIMEOUT'\n | 'ABORTED'\n | 'BAD_REQUEST'\n | 'UNSUPPORTED_PLATFORM'\n | 'UNKNOWN';\n\nexport interface RequestErrorOptions {\n code: RequestErrorCode;\n message: string;\n /** 出错的请求配置 */\n config: RequestConfig;\n /** 出错时已拿到的响应(如响应处理阶段失败) */\n response?: RequestResponse;\n /** 底层原因(原生异常 / 平台 fail 对象等) */\n cause?: unknown;\n}\n\n/**\n * 统一请求错误:适配器与拦截器抛出的错误全部归一化为该类型,\n * 便于调用方按 code 分支处理(超时重试、取消忽略、网络提示等)。\n */\nexport class RequestError extends Error {\n readonly code: RequestErrorCode;\n readonly config: RequestConfig;\n readonly response?: RequestResponse;\n readonly cause?: unknown;\n\n public constructor(options: RequestErrorOptions) {\n super(options.message);\n this.name = 'RequestError';\n this.code = options.code;\n this.config = options.config;\n this.response = options.response;\n this.cause = options.cause;\n }\n}\n\nexport function isRequestError(error: unknown): error is RequestError {\n return error instanceof RequestError;\n}\n",
|
|
36
|
+
"/**\n * request 模块纯函数工具:URL 拼接、query 序列化、请求头合并、请求体序列化。\n * 不依赖任何平台全局,便于单测。\n */\nimport type { HeaderValue, HttpMethod } from './types';\n\nconst HTTP_METHODS: ReadonlySet<string> = new Set([\n 'GET',\n 'POST',\n 'PUT',\n 'DELETE',\n 'PATCH',\n 'HEAD',\n 'OPTIONS',\n]);\n\n/** 归一化 HTTP 方法为大写;缺省为 GET,非法方法抛错 */\nexport function normalizeMethod(method: string | undefined): HttpMethod {\n if (method === undefined) {\n return 'GET';\n }\n const normalized = method.toUpperCase();\n if (!HTTP_METHODS.has(normalized)) {\n throw new Error(`Unsupported HTTP method: ${method}`);\n }\n return normalized as HttpMethod;\n}\n\nconst ABSOLUTE_URL_RE = /^[a-z][a-z\\d+.-]*:\\/\\//i;\n\n/** 是否为带协议的绝对地址(http:// / https:// 等) */\nexport function isAbsoluteURL(url: string): boolean {\n return ABSOLUTE_URL_RE.test(url);\n}\n\n/** 拼接 baseURL 与相对地址,去除重复斜杠;绝对地址直接返回 */\nexport function combineURLs(baseURL: string | undefined, url: string): string {\n if (!baseURL || isAbsoluteURL(url)) {\n return url;\n }\n return `${baseURL.replace(/\\/+$/, '')}/${url.replace(/^\\/+/, '')}`;\n}\n\nfunction formatParamValue(value: unknown): string {\n if (value instanceof Date) {\n return value.toISOString();\n }\n if (typeof value === 'object') {\n return JSON.stringify(value);\n }\n return String(value);\n}\n\n/**\n * 序列化 query 参数:null / undefined 跳过,数组展开为重复键,\n * Date 转 ISO 字符串,对象 JSON 序列化。\n */\nexport function serializeParams(\n params: Record<string, unknown> | undefined\n): string {\n if (!params) {\n return '';\n }\n const parts: string[] = [];\n Object.keys(params).forEach((key) => {\n const value = params[key];\n if (value === undefined || value === null) {\n return;\n }\n const encodedKey = encodeURIComponent(key);\n if (Array.isArray(value)) {\n value.forEach((item) => {\n if (item === undefined || item === null) {\n return;\n }\n parts.push(\n `${encodedKey}=${encodeURIComponent(formatParamValue(item))}`\n );\n });\n return;\n }\n parts.push(`${encodedKey}=${encodeURIComponent(formatParamValue(value))}`);\n });\n return parts.join('&');\n}\n\n/** 拼接完整请求地址:baseURL + url + query 参数 */\nexport function buildURL(\n baseURL: string | undefined,\n url: string,\n params: Record<string, unknown> | undefined\n): string {\n const full = combineURLs(baseURL, url);\n const query = serializeParams(params);\n if (!query) {\n return full;\n }\n const separator = full.includes('?') ? '&' : '?';\n return `${full}${separator}${query}`;\n}\n\nfunction normalizeHeaderValue(value: HeaderValue): string | undefined {\n if (value === undefined || value === null) {\n return undefined;\n }\n return String(value);\n}\n\n/**\n * 大小写不敏感合并请求头:多个来源按顺序合并,后出现的同名头(忽略大小写)\n * 覆盖先前的,键名保留后出现来源的原始写法。\n */\nexport function mergeHeaders(\n ...sources: Array<Record<string, HeaderValue> | undefined>\n): Record<string, string> {\n const merged: Record<string, string> = {};\n const lowerToKey = new Map<string, string>();\n\n sources.forEach((source) => {\n if (!source) {\n return;\n }\n Object.keys(source).forEach((key) => {\n const value = normalizeHeaderValue(source[key]);\n if (value === undefined) {\n return;\n }\n const lower = key.toLowerCase();\n const previous = lowerToKey.get(lower);\n if (previous !== undefined && previous !== key) {\n delete merged[previous];\n }\n lowerToKey.set(lower, key);\n merged[key] = value;\n });\n });\n\n return merged;\n}\n\nfunction hasHeader(headers: Record<string, string>, name: string): boolean {\n return Object.keys(headers).some(\n (key) => key.toLowerCase() === name.toLowerCase()\n );\n}\n\n/**\n * 序列化请求体(Web 端):\n * - GET / HEAD 无请求体\n * - 字符串、FormData、Blob、ArrayBuffer / TypedArray 原样透传\n * - 普通对象 / 数组 JSON 序列化,并自动设置 Content-Type: application/json\n */\nexport function serializeBody(\n data: unknown,\n headers: Record<string, string>,\n method: HttpMethod\n): BodyInit | undefined {\n if (method === 'GET' || method === 'HEAD') {\n return undefined;\n }\n if (data === undefined || data === null) {\n return undefined;\n }\n if (typeof data === 'string') {\n return data;\n }\n if (typeof FormData !== 'undefined' && data instanceof FormData) {\n return data;\n }\n if (typeof Blob !== 'undefined' && data instanceof Blob) {\n return data;\n }\n if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) {\n return data as BodyInit;\n }\n if (!hasHeader(headers, 'Content-Type')) {\n headers['Content-Type'] = 'application/json';\n }\n return JSON.stringify(data);\n}\n",
|
|
37
|
+
"import { RequestError } from '../errors';\nimport {\n buildURL,\n mergeHeaders,\n normalizeMethod,\n serializeBody,\n} from '../helpers';\nimport type { RequestAdapter, RequestConfig, RequestResponse } from '../types';\n\n/**\n * Web 端适配器(策略):基于 fetch。\n *\n * - 超时:AbortController + timer,超时归类 TIMEOUT\n * - 取消:config.signal 与超时共用同一个 AbortController,外部取消归类 ABORTED\n * - 响应体:arraybuffer 返回 ArrayBuffer;json(默认)尝试解析、失败回退原文;text 返回原文\n */\nexport class FetchAdapter implements RequestAdapter {\n public readonly platform = 'web' as const;\n\n public async request<T>(\n config: RequestConfig<T>\n ): Promise<RequestResponse<T>> {\n const method = normalizeMethod(config.method);\n const url = buildURL(config.baseURL, config.url, config.params);\n const headers = mergeHeaders(config.headers);\n const body = serializeBody(config.data, headers, method);\n const controller = new AbortController();\n let timedOut = false;\n let externalAborted = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n if (config.signal) {\n if (config.signal.aborted) {\n externalAborted = true;\n controller.abort();\n } else {\n config.signal.addEventListener(\n 'abort',\n () => {\n externalAborted = true;\n controller.abort();\n },\n { once: true }\n );\n }\n }\n\n if (typeof config.timeout === 'number' && config.timeout > 0) {\n timer = setTimeout(() => {\n timedOut = true;\n controller.abort();\n }, config.timeout);\n }\n\n const credentials: RequestCredentials =\n config.credentials ??\n (config.withCredentials === true ? 'include' : 'same-origin');\n\n try {\n const response = await fetch(url, {\n method,\n headers,\n body,\n signal: controller.signal,\n credentials,\n });\n\n const responseHeaders: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n responseHeaders[key] = value;\n });\n\n const data = await parseFetchData(response, config);\n return {\n data: data as T,\n statusCode: response.status,\n statusText: response.statusText,\n headers: responseHeaders,\n config,\n };\n } catch (error) {\n if (timedOut) {\n throw new RequestError({\n code: 'TIMEOUT',\n message: `Request timeout after ${String(config.timeout)}ms`,\n config,\n cause: error,\n });\n }\n if (externalAborted || controller.signal.aborted) {\n throw new RequestError({\n code: 'ABORTED',\n message: 'Request aborted',\n config,\n cause: error,\n });\n }\n throw new RequestError({\n code: 'NETWORK_ERROR',\n message: error instanceof Error ? error.message : String(error),\n config,\n cause: error,\n });\n } finally {\n if (timer !== undefined) {\n clearTimeout(timer);\n }\n }\n }\n}\n\nasync function parseFetchData(\n response: Response,\n config: RequestConfig\n): Promise<unknown> {\n if (config.responseType === 'arraybuffer') {\n return response.arrayBuffer();\n }\n const text = await response.text();\n if (config.dataType === 'text' || !text) {\n return text;\n }\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n",
|
|
38
|
+
"import type { DetectedPlatform } from '../types';\n\ntype Host = Record<string, unknown>;\n\n/** 各端安全访问全局对象:优先 globalThis(微信基础库 2.2.3+ 可用),兜底 self / global */\nfunction getHost(): Host {\n if (typeof globalThis !== 'undefined') {\n return globalThis as unknown as Host;\n }\n if (typeof self !== 'undefined') {\n return self as unknown as Host;\n }\n if (typeof global !== 'undefined') {\n return global as unknown as Host;\n }\n return {};\n}\n\n/** 小程序请求全局的公共形状(wx.request / my.request / tt.request) */\nexport interface MiniProgramRequestApi {\n request?: (options: Record<string, unknown>) => { abort?: () => void };\n}\n\n/**\n * 读取小程序请求全局(wx / my / tt)。\n * 通过全局对象属性访问而非直接引用全局名,避免污染全局声明、\n * 也避免与用户安装的小程序官方类型(如 miniprogram-api-typings)冲突。\n */\nexport function getMiniProgramApi(\n name: 'wx' | 'my' | 'tt'\n): MiniProgramRequestApi | null {\n const api = getHost()[name];\n return typeof api === 'object' && api !== null\n ? (api as MiniProgramRequestApi)\n : null;\n}\n\n/**\n * 运行时探测目标端:\n * 小程序全局优先(wx → my → tt),其次 Web fetch;均不可用返回 'unknown'。\n */\nexport function detectPlatform(): DetectedPlatform {\n if (hasRequestApi('wx')) {\n return 'weixin';\n }\n if (hasRequestApi('my')) {\n return 'alipay';\n }\n if (hasRequestApi('tt')) {\n return 'bytedance';\n }\n if (typeof fetch === 'function') {\n return 'web';\n }\n return 'unknown';\n}\n\nfunction hasRequestApi(name: 'wx' | 'my' | 'tt'): boolean {\n const api = getMiniProgramApi(name);\n return api !== null && typeof api.request === 'function';\n}\n",
|
|
39
|
+
"import { RequestError, type RequestErrorCode } from '../errors';\nimport { buildURL, mergeHeaders } from '../helpers';\nimport type {\n HeaderValue,\n RequestAdapter,\n RequestConfig,\n RequestResponse,\n} from '../types';\nimport { getMiniProgramApi } from './platform';\n\n/**\n * 小程序差异层:微信 / 阿里 / 字节的请求 API 语义一致(wx.request /\n * my.request / tt.request),仅字段名与回调形状不同。与 CLI 的 MpDialect\n * 同思路,把差异收敛为参数化方言,避免三个适配器重复实现。\n */\nexport interface MiniProgramDialect {\n /** 目标端标识 */\n platform: 'weixin' | 'alipay' | 'bytedance';\n /** 全局对象名:wx / my / tt */\n globalName: 'wx' | 'my' | 'tt';\n /** 请求参数中请求头字段名:微信/字节 header,阿里 headers */\n headerField: 'header' | 'headers';\n /** 成功回调中状态码字段:微信/字节 statusCode,阿里 status */\n statusField: 'statusCode' | 'status';\n /** 成功回调中响应头字段名:微信/字节 header,阿里 headers */\n responseHeaderField: 'header' | 'headers';\n}\n\nexport const WEIXIN_DIALECT: MiniProgramDialect = {\n platform: 'weixin',\n globalName: 'wx',\n headerField: 'header',\n statusField: 'statusCode',\n responseHeaderField: 'header',\n};\n\nexport const ALIPAY_DIALECT: MiniProgramDialect = {\n platform: 'alipay',\n globalName: 'my',\n headerField: 'headers',\n statusField: 'status',\n responseHeaderField: 'headers',\n};\n\nexport const BYTEDANCE_DIALECT: MiniProgramDialect = {\n platform: 'bytedance',\n globalName: 'tt',\n headerField: 'header',\n statusField: 'statusCode',\n responseHeaderField: 'header',\n};\n\n/**\n * 小程序端适配器(策略):wx.request / my.request / tt.request 的统一封装。\n *\n * - 超时:手动 timer + 底层任务 abort,三端行为一致;成功/失败回调都会清理 timer\n * - 取消:config.signal 触发时 abort 底层任务\n * - 失败归一化:按平台 errMsg 关键字归类 TIMEOUT / ABORTED / NETWORK_ERROR\n */\nexport class MiniProgramAdapter implements RequestAdapter {\n public readonly platform: 'weixin' | 'alipay' | 'bytedance';\n\n public constructor(private readonly dialect: MiniProgramDialect) {\n this.platform = dialect.platform;\n }\n\n public request<T>(config: RequestConfig<T>): Promise<RequestResponse<T>> {\n const api = getMiniProgramApi(this.dialect.globalName);\n if (!api || typeof api.request !== 'function') {\n return Promise.reject(\n new RequestError({\n code: 'UNSUPPORTED_PLATFORM',\n message: `${this.dialect.globalName}.request is not available in current environment`,\n config,\n })\n );\n }\n // 守卫后收窄为函数;Promise 执行器闭包内无法复用前面的窄化\n const requestFn = api.request;\n\n return new Promise<RequestResponse<T>>((resolve, reject) => {\n let timedOut = false;\n let settled = false;\n let task: { abort?: () => void } | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n // 保证成功/失败/超时只结算一次,并清理超时 timer\n const settle = (fn: () => void): void => {\n if (settled) {\n return;\n }\n settled = true;\n if (timer !== undefined) {\n clearTimeout(timer);\n }\n fn();\n };\n\n const options: Record<string, unknown> = {\n url: buildURL(config.baseURL, config.url, config.params),\n method: config.method ?? 'GET',\n data: config.data,\n [this.dialect.headerField]: normalizeHeaders(config.headers),\n dataType: config.dataType ?? 'json',\n ...(config.responseType !== undefined\n ? { responseType: config.responseType }\n : {}),\n ...(config.timeout !== undefined && config.timeout > 0\n ? { timeout: config.timeout }\n : {}),\n success: (res: Record<string, unknown>) => {\n settle(() => {\n const rawStatus = res[this.dialect.statusField];\n const statusCode = Number(rawStatus ?? res.statusCode ?? 0);\n const responseHeaders = normalizeResponseHeaders(\n res[this.dialect.responseHeaderField]\n );\n resolve({\n data: res.data as T,\n statusCode,\n headers: responseHeaders,\n config,\n ...(Array.isArray(res.cookies)\n ? { cookies: res.cookies as string[] }\n : {}),\n ...(typeof res.errMsg === 'string' ? { errMsg: res.errMsg } : {}),\n });\n });\n },\n fail: (err: Record<string, unknown>) => {\n settle(() => {\n const errMsg = extractFailMessage(err);\n reject(\n new RequestError({\n code: timedOut ? 'TIMEOUT' : classifyMpError(errMsg),\n message: errMsg || 'Network request failed',\n config,\n cause: err,\n })\n );\n });\n },\n };\n\n if (config.timeout !== undefined && config.timeout > 0) {\n timer = setTimeout(() => {\n timedOut = true;\n task?.abort?.();\n }, config.timeout);\n }\n\n task = requestFn(options) ?? undefined;\n\n if (config.signal) {\n if (config.signal.aborted) {\n task?.abort?.();\n } else {\n config.signal.addEventListener(\n 'abort',\n () => {\n task?.abort?.();\n },\n { once: true }\n );\n }\n }\n });\n }\n}\n\nfunction normalizeHeaders(\n headers: Record<string, HeaderValue> | undefined\n): Record<string, string> {\n return mergeHeaders(headers);\n}\n\nfunction normalizeResponseHeaders(value: unknown): Record<string, string> {\n if (typeof value !== 'object' || value === null) {\n return {};\n }\n const result: Record<string, string> = {};\n Object.keys(value as Record<string, unknown>).forEach((key) => {\n const item = (value as Record<string, unknown>)[key];\n if (item !== undefined && item !== null) {\n result[key] = String(item);\n }\n });\n return result;\n}\n\nfunction extractFailMessage(err: Record<string, unknown>): string {\n if (typeof err.errMsg === 'string') {\n return err.errMsg;\n }\n if (typeof err.errorMessage === 'string') {\n return err.errorMessage;\n }\n if (typeof err.error === 'string') {\n return err.error;\n }\n return '';\n}\n\n/** 按平台 errMsg 关键字归类错误码 */\nfunction classifyMpError(errMsg: string): RequestErrorCode {\n if (!errMsg) {\n return 'UNKNOWN';\n }\n if (/timeout/i.test(errMsg)) {\n return 'TIMEOUT';\n }\n if (/abort/i.test(errMsg)) {\n return 'ABORTED';\n }\n return 'NETWORK_ERROR';\n}\n",
|
|
40
|
+
"import { RequestError } from '../errors';\nimport type {\n DetectedPlatform,\n RequestAdapter,\n RequestAdapterResolver,\n RequestConfig,\n RequestPlatform,\n} from '../types';\nimport { FetchAdapter } from './fetch';\nimport {\n ALIPAY_DIALECT,\n BYTEDANCE_DIALECT,\n MiniProgramAdapter,\n WEIXIN_DIALECT,\n} from './mp';\nimport { detectPlatform } from './platform';\n\nexport { detectPlatform } from './platform';\nexport type { MiniProgramRequestApi } from './platform';\nexport type { MiniProgramDialect } from './mp';\n\n/** 各端适配器注册表:'auto' 不在此列,由运行时探测决定 */\nexport const adapters: Record<\n Exclude<RequestPlatform, 'auto'>,\n RequestAdapter\n> = {\n web: new FetchAdapter(),\n weixin: new MiniProgramAdapter(WEIXIN_DIALECT),\n alipay: new MiniProgramAdapter(ALIPAY_DIALECT),\n bytedance: new MiniProgramAdapter(BYTEDANCE_DIALECT),\n};\n\n/** 按目标端创建适配器;不支持的端抛 UNSUPPORTED_PLATFORM */\nexport function createAdapter(\n platform: Exclude<RequestPlatform, 'auto'>\n): RequestAdapter {\n const adapter = adapters[platform];\n if (!adapter) {\n throw new RequestError({\n code: 'UNSUPPORTED_PLATFORM',\n message: `Unsupported request platform: ${String(platform)}`,\n config: { url: '' },\n });\n }\n return adapter;\n}\n\n/**\n * 解析请求应使用的适配器(优先级从高到低):\n * 1. 请求级 config.platform(显式指定非 auto 时)\n * 2. 实例级自定义 adapter(对象或解析函数)\n * 3. 实例级默认 platform;仍为 auto 时运行时探测\n */\nexport async function resolveAdapter(\n config: RequestConfig,\n instanceAdapter?: RequestAdapter | RequestAdapterResolver,\n instancePlatform?: RequestPlatform\n): Promise<RequestAdapter> {\n if (config.platform !== undefined && config.platform !== 'auto') {\n return createAdapter(config.platform);\n }\n if (instanceAdapter !== undefined) {\n return typeof instanceAdapter === 'function'\n ? await instanceAdapter(config)\n : instanceAdapter;\n }\n if (instancePlatform !== undefined && instancePlatform !== 'auto') {\n return createAdapter(instancePlatform);\n }\n const detected: DetectedPlatform = detectPlatform();\n if (detected === 'unknown') {\n throw new RequestError({\n code: 'UNSUPPORTED_PLATFORM',\n message:\n 'No supported request platform detected (web fetch / wx.request / my.request / tt.request)',\n config,\n });\n }\n return createAdapter(detected);\n}\n",
|
|
41
|
+
"import type {\n InterceptorFulfilled,\n InterceptorHandler,\n InterceptorRejected,\n} from './types';\n\n/**\n * 拦截器管理器:维护同类型(请求 / 响应)拦截器列表。\n * - use 注册并返回 id,eject 按 id 移除(软删除,保留占位)\n * - forEach 跳过已移除的项\n */\nexport class InterceptorManager<T> {\n private readonly handlers: Array<InterceptorHandler<T> | null> = [];\n\n public use(\n onFulfilled?: InterceptorFulfilled<T>,\n onRejected?: InterceptorRejected\n ): number {\n this.handlers.push({ onFulfilled, onRejected });\n return this.handlers.length - 1;\n }\n\n public eject(id: number): void {\n if (this.handlers[id] !== undefined) {\n this.handlers[id] = null;\n }\n }\n\n public clear(): void {\n this.handlers.length = 0;\n }\n\n public forEach(\n fn: (handler: InterceptorHandler<T>, id: number) => void\n ): void {\n this.handlers.forEach((handler, id) => {\n if (handler !== null) {\n fn(handler, id);\n }\n });\n }\n}\n",
|
|
42
|
+
"import { resolveAdapter } from './adapters';\nimport { RequestError } from './errors';\nimport { mergeHeaders, normalizeMethod } from './helpers';\nimport { InterceptorManager } from './interceptors';\nimport type {\n RequestAdapter,\n RequestConfig,\n RequestOptions,\n RequestResponse,\n} from './types';\n\ntype ChainStep = (value: unknown) => unknown;\n\n/**\n * 统一请求客户端:跨端请求入口。\n *\n * 职责:\n * - 实例级默认值(baseURL / headers / timeout / platform 等)与单次配置合并\n * - 请求拦截器(注册顺序)→ 平台适配器 → 响应拦截器(后注册先执行)的链路编排\n * - 目标端解析:config.platform > 实例 adapter > 实例 platform > 运行时探测\n *\n * 用法:\n * ```ts\n * const http = createRequest({ baseURL: '/api', timeout: 10000 });\n * http.interceptors.request.use((config) => {\n * config.headers.Authorization = `Bearer ${token}`;\n * return config;\n * });\n * const res = await http.get<{ list: Item[] }>('/items');\n * ```\n */\nexport class Request {\n /** 请求 / 响应拦截器管理器 */\n public readonly interceptors: {\n request: InterceptorManager<RequestConfig>;\n response: InterceptorManager<RequestResponse>;\n } = {\n request: new InterceptorManager<RequestConfig>(),\n response: new InterceptorManager<RequestResponse>(),\n };\n\n private readonly instanceDefaults: RequestOptions;\n private readonly instanceAdapter?: RequestOptions['adapter'];\n private readonly instancePlatform?: RequestOptions['platform'];\n\n public constructor(options: RequestOptions = {}) {\n const {\n adapter: instanceAdapter,\n platform: instancePlatform,\n ...rest\n } = options;\n this.instanceAdapter = instanceAdapter;\n this.instancePlatform = instancePlatform;\n this.instanceDefaults = { ...rest };\n }\n\n /** 发起请求:合并配置 → 请求拦截器 → 适配器 → 响应拦截器 */\n public request<T = unknown>(\n config: RequestConfig<T>\n ): Promise<RequestResponse<T>> {\n let merged: RequestConfig<T>;\n try {\n merged = this.mergeConfig(config);\n } catch (error) {\n // 配置非法统一走 rejected promise,调用方用 .catch / try-await 即可处理\n return Promise.reject(error);\n }\n\n // 拦截器链:请求拦截器按注册顺序在最前,适配器居中进行实际请求,\n // 响应拦截器后注册的先执行(离调用方最近,最外层)。\n // 链以 (fulfilled, rejected) 成对消费;onRejected 捕获的是其之前链路的错误。\n const chain: Array<ChainStep | undefined> = [];\n this.interceptors.request.forEach((handler) => {\n chain.push(\n handler.onFulfilled as unknown as ChainStep,\n handler.onRejected as ChainStep | undefined\n );\n });\n chain.push(\n (value: unknown) => this.dispatch(value as RequestConfig<T>),\n undefined\n );\n const responseHandlers: Array<{\n onFulfilled?: ChainStep;\n onRejected?: ChainStep | undefined;\n }> = [];\n this.interceptors.response.forEach((handler) => {\n responseHandlers.push({\n onFulfilled: handler.onFulfilled as unknown as ChainStep,\n onRejected: handler.onRejected as ChainStep | undefined,\n });\n });\n for (let index = responseHandlers.length - 1; index >= 0; index -= 1) {\n const handler = responseHandlers[index];\n chain.push(handler.onFulfilled, handler.onRejected);\n }\n\n let promise: Promise<unknown> = Promise.resolve(merged);\n while (chain.length > 0) {\n const fulfilled = chain.shift();\n const rejected = chain.shift();\n promise = promise.then(\n fulfilled as ((value: unknown) => unknown) | undefined,\n rejected as ((reason: unknown) => unknown) | undefined\n );\n }\n return promise as Promise<RequestResponse<T>>;\n }\n\n public get<T = unknown>(\n url: string,\n config?: Omit<RequestConfig, 'url' | 'method'>\n ): Promise<RequestResponse<T>> {\n return this.request<T>({ ...config, url, method: 'GET' });\n }\n\n public delete<T = unknown>(\n url: string,\n config?: Omit<RequestConfig, 'url' | 'method'>\n ): Promise<RequestResponse<T>> {\n return this.request<T>({ ...config, url, method: 'DELETE' });\n }\n\n public head<T = unknown>(\n url: string,\n config?: Omit<RequestConfig, 'url' | 'method'>\n ): Promise<RequestResponse<T>> {\n return this.request<T>({ ...config, url, method: 'HEAD' });\n }\n\n public options<T = unknown>(\n url: string,\n config?: Omit<RequestConfig, 'url' | 'method'>\n ): Promise<RequestResponse<T>> {\n return this.request<T>({ ...config, url, method: 'OPTIONS' });\n }\n\n public post<T = unknown>(\n url: string,\n data?: T,\n config?: Omit<RequestConfig<T>, 'url' | 'method' | 'data'>\n ): Promise<RequestResponse<T>> {\n return this.request<T>({ ...config, url, method: 'POST', data });\n }\n\n public put<T = unknown>(\n url: string,\n data?: T,\n config?: Omit<RequestConfig<T>, 'url' | 'method' | 'data'>\n ): Promise<RequestResponse<T>> {\n return this.request<T>({ ...config, url, method: 'PUT', data });\n }\n\n public patch<T = unknown>(\n url: string,\n data?: T,\n config?: Omit<RequestConfig<T>, 'url' | 'method' | 'data'>\n ): Promise<RequestResponse<T>> {\n return this.request<T>({ ...config, url, method: 'PATCH', data });\n }\n\n private dispatch<T>(config: RequestConfig<T>): Promise<RequestResponse<T>> {\n return resolveAdapter(\n config,\n this.instanceAdapter,\n this.instancePlatform\n ).then((adapter: RequestAdapter) => adapter.request<T>(config));\n }\n\n /** 合并实例级默认值与单次配置,并校验 url / method */\n private mergeConfig<T>(config: RequestConfig<T>): RequestConfig<T> {\n if (!config || typeof config !== 'object') {\n throw new RequestError({\n code: 'BAD_REQUEST',\n message: 'Request config must be an object',\n config: config as RequestConfig,\n });\n }\n\n const merged: RequestConfig = {\n ...this.instanceDefaults,\n ...config,\n headers: mergeHeaders(this.instanceDefaults.headers, config.headers),\n params: { ...this.instanceDefaults.params, ...config.params },\n };\n\n if (typeof merged.url !== 'string' || merged.url.length === 0) {\n throw new RequestError({\n code: 'BAD_REQUEST',\n message: 'Request url is required',\n config: merged,\n });\n }\n\n try {\n merged.method = normalizeMethod(merged.method);\n } catch (cause) {\n throw new RequestError({\n code: 'BAD_REQUEST',\n message: (cause as Error).message,\n config: merged,\n cause,\n });\n }\n\n return merged as RequestConfig<T>;\n }\n}\n",
|
|
43
|
+
"import { Request } from './request';\nimport type { RequestOptions } from './types';\n\n/** 创建独立请求实例;不传配置时等价于使用默认实例 */\nexport function createRequest(options: RequestOptions = {}): Request {\n return new Request(options);\n}\n\n/**\n * 全局默认请求实例:直接使用无需创建。\n * ```ts\n * import { request } from 'transone/request';\n * const res = await request.get('/api/items');\n * ```\n */\nexport const request: Request = new Request();\n\nexport { Request } from './request';\nexport { InterceptorManager } from './interceptors';\nexport { RequestError, isRequestError } from './errors';\nexport type { RequestErrorCode, RequestErrorOptions } from './errors';\nexport {\n adapters,\n createAdapter,\n detectPlatform,\n resolveAdapter,\n} from './adapters';\nexport type { MiniProgramDialect, MiniProgramRequestApi } from './adapters';\nexport type {\n DetectedPlatform,\n HeaderValue,\n HttpMethod,\n InterceptorFulfilled,\n InterceptorHandler,\n InterceptorRejected,\n RequestAdapter,\n RequestAdapterResolver,\n RequestConfig,\n RequestDataType,\n RequestOptions,\n RequestPlatform,\n RequestResponse,\n RequestResponseType,\n} from './types';\n",
|
|
44
|
+
"/**\n * 可选集成:基于 transone Component 的声明式图表组件 TcChart。\n *\n * 与 transone 组件系统集成,一份源码同时编译 Web 与小程序:\n * - Web:canvas 元素由组件渲染,onMounted 后自动解析并渲染\n * - 小程序:canvas 编译为 <canvas type=\"2d\">,通过 SelectorQuery 获取节点\n * - 未来原生:通过 resolve 注入自定义解析器\n *\n * 组件本身是薄壳:状态由父级以 option 传入(完全受控),\n * 数据变化时自动 setOption + render。\n */\n\nimport {\n Component,\n h,\n type VNode,\n} from 'transone';\nimport type { ChartRenderContext, ChartOption } from './types';\nimport type { ChartBase } from './core/chart';\nimport { createChart } from './factory';\nimport {\n detectMiniProgramGlobal,\n getMiniProgramCanvasNode,\n resolveMiniProgramCanvas,\n} from './adapters/miniprogram';\nimport { resolveWebCanvas } from './adapters/web';\n\nexport interface TcChartProps {\n /** 图表配置(line / bar / pie / radar)。 */\n option: ChartOption;\n /** 自定义解析器:把 canvas 元素/节点解析为渲染上下文。 */\n resolve?: (\n element: unknown\n ) => Promise<ChartRenderContext> | ChartRenderContext;\n className?: string;\n}\n\ninterface TcChartState {\n canvasId: string;\n}\n\n/** canvas 元素 id:Web 端仅作 DOM 属性;小程序端配合 SelectorQuery .in(实例) 隔离查询,多实例共存不冲突。 */\nconst CANVAS_ID = 'tc-chart-canvas';\n\nexport class TcChart extends Component<TcChartProps, TcChartState> {\n private chart: ChartBase<ChartOption> | null = null;\n private destroyed = false;\n\n protected initState(): TcChartState {\n return { canvasId: CANVAS_ID };\n }\n\n protected initStyles(): void {\n this.styleManager.addStyle('tc-chart-canvas', {\n selector: '.tc-chart__canvas',\n properties: {\n display: 'block',\n height: '100%',\n width: '100%',\n },\n });\n }\n\n protected render(): VNode {\n return h('canvas', {\n id: this.state.canvasId,\n className: `tc-chart__canvas${this.props.className ? ` ${this.props.className}` : ''}`,\n type: '2d',\n });\n }\n\n protected onMounted(): void {\n this.attachChart();\n }\n\n protected onUpdated(): void {\n if (this.chart && !this.destroyed) {\n this.chart.setOption(this.props.option).render();\n } else if (!this.chart && !this.destroyed) {\n this.attachChart();\n }\n }\n\n protected onUnmounted(): void {\n this.destroyed = true;\n this.chart?.destroy();\n this.chart = null;\n }\n\n /** 对外暴露图表实例(高级用法:手动 resize / 订阅事件)。 */\n public getChart(): ChartBase<ChartOption> | null {\n return this.chart;\n }\n\n private attachChart(): void {\n if (this.destroyed) {\n return;\n }\n this.resolveContext()\n .then((context) => {\n if (this.destroyed) {\n return;\n }\n this.chart = createChart(context, this.props.option);\n this.chart.render();\n })\n .catch((error: unknown) => {\n console.error('[TcChart] attach failed:', error);\n });\n }\n\n /** 组件根元素(canvas)。Web 端用于解析渲染上下文;小程序端经 SelectorQuery .in(实例) 查询,不依赖此方法。 */\n public getElement(): HTMLCanvasElement | null {\n return super.getElement() as HTMLCanvasElement | null;\n }\n\n private resolveContext(): Promise<ChartRenderContext> {\n if (this.props.resolve) {\n return Promise.resolve(this.props.resolve(this.getElement()));\n }\n\n const platform = detectMiniProgramGlobal();\n if (platform) {\n // 小程序:SelectorQuery 查询节点(组件可能尚未渲染完,有限重试)\n const selector = `#${this.state.canvasId}`;\n const attempt = (left: number): Promise<ChartRenderContext> =>\n getMiniProgramCanvasNode({\n platform,\n instance: this,\n selector,\n })\n .then((node) => resolveMiniProgramCanvas(node))\n .catch((error: unknown) => {\n if (left <= 0) {\n throw error;\n }\n return new Promise((resolve) => setTimeout(resolve, 50)).then(() =>\n attempt(left - 1)\n );\n });\n return attempt(5);\n }\n\n // Web:canvas 元素即 HTMLCanvasElement\n return Promise.resolve(\n resolveWebCanvas(this.getElement() as unknown as HTMLCanvasElement)\n );\n }\n}\n"
|
|
45
|
+
],
|
|
46
|
+
"mappings": "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",
|
|
47
|
+
"debugId": "71613FE96B28FF3C64756E2164756E21",
|
|
48
|
+
"names": []
|
|
49
|
+
}
|