From ee65f9f897d17a2a96fd163a295ad687c853aead Mon Sep 17 00:00:00 2001 From: Princelion33 Date: Thu, 11 Jun 2026 21:24:52 +0200 Subject: [PATCH 1/3] fix(figure): add and register missing rectText figure to resolve text annotations disappearing --- src/extension/rectText.ts | 119 ++++++++++++++++++++++++++++++++++++++ src/index.ts | 5 +- 2 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 src/extension/rectText.ts diff --git a/src/extension/rectText.ts b/src/extension/rectText.ts new file mode 100644 index 00000000..c92d145a --- /dev/null +++ b/src/extension/rectText.ts @@ -0,0 +1,119 @@ +import { FigureTemplate } from 'klinecharts' + +export interface RectTextAttrs { + x: number + y: number + text: string + align?: CanvasTextAlign + baseline?: CanvasTextBaseline +} + +export interface RectTextStyle { + color?: string + size?: number + family?: string + weight?: string | number + style?: 'fill' | 'stroke' | 'stroke_fill' + backgroundColor?: string + borderColor?: string + borderSize?: number + borderRadius?: number + paddingLeft?: number + paddingRight?: number + paddingTop?: number + paddingBottom?: number +} + +const rectText: FigureTemplate = { + name: 'rectText', + draw: (ctx: CanvasRenderingContext2D, attrs: RectTextAttrs, styles: RectTextStyle) => { + try { + if (!ctx || !attrs) return + const { x, y, text, align = 'center', baseline = 'middle' } = attrs + if (typeof x !== 'number' || typeof y !== 'number' || text === undefined || text === null) return + + const textStr = String(text) + if (!textStr.trim()) return + + const safeStyles = styles || {} + const fontSize = Number(safeStyles.size) || 12 + ctx.font = `${fontSize}px Arial` + const textWidth = ctx.measureText(textStr).width + const textHeight = fontSize + + const paddingLeft = Number(safeStyles.paddingLeft) ?? 8 + const paddingRight = Number(safeStyles.paddingRight) ?? 8 + const paddingTop = Number(safeStyles.paddingTop) ?? 4 + const paddingBottom = Number(safeStyles.paddingBottom) ?? 4 + + const rectWidth = textWidth + paddingLeft + paddingRight + const rectHeight = textHeight + paddingTop + paddingBottom + + let rectX: number, textX: number + if (align === 'left') { + rectX = x + textX = x + paddingLeft + } else if (align === 'right') { + rectX = x - rectWidth + textX = x - paddingRight + } else { // center + rectX = x - rectWidth / 2 + textX = x + } + + let rectY: number, textY: number + if (baseline === 'top') { + rectY = y + textY = y + paddingTop + textHeight / 2 + } else if (baseline === 'bottom') { + rectY = y - rectHeight + textY = y - paddingBottom - textHeight / 2 + } else { // middle + rectY = y - rectHeight / 2 + textY = y + } + + if (isNaN(rectX) || isNaN(rectY) || isNaN(rectWidth) || isNaN(rectHeight) || isNaN(textX) || isNaN(textY)) { + return + } + + ctx.save() + ctx.beginPath() + const radius = Math.max(0, Number(safeStyles.borderRadius) || 0) + + if ((ctx as any).roundRect) { + (ctx as any).roundRect(rectX, rectY, rectWidth, rectHeight, radius) + } else { + ctx.moveTo(rectX + radius, rectY) + ctx.lineTo(rectX + rectWidth - radius, rectY) + ctx.quadraticCurveTo(rectX + rectWidth, rectY, rectX + rectWidth, rectY + radius) + ctx.lineTo(rectX + rectWidth, rectY + rectHeight - radius) + ctx.quadraticCurveTo(rectX + rectWidth, rectY + rectHeight, rectX + rectWidth - radius, rectY + rectHeight) + ctx.lineTo(rectX + radius, rectY + rectHeight) + ctx.quadraticCurveTo(rectX, rectY + rectHeight, rectX, rectY + rectHeight - radius) + ctx.lineTo(rectX, rectY + radius) + ctx.quadraticCurveTo(rectX, rectY, rectX + radius, rectY) + } + + if (safeStyles.style === 'fill' || safeStyles.style === 'stroke_fill') { + ctx.fillStyle = safeStyles.backgroundColor || 'rgba(22, 119, 255, 0.15)' + ctx.fill() + } + if (safeStyles.style === 'stroke' || safeStyles.style === 'stroke_fill') { + ctx.strokeStyle = safeStyles.borderColor || 'rgba(22, 119, 255, 0.6)' + ctx.lineWidth = Number(safeStyles.borderSize) || 1 + ctx.stroke() + } + + ctx.fillStyle = safeStyles.color || '#1677FF' + ctx.textAlign = align + ctx.textBaseline = 'middle' + ctx.fillText(textStr, textX, textY) + ctx.restore() + } catch (err) { + console.error("Error in custom rectText draw:", err) + } + } +} + +export default rectText diff --git a/src/index.ts b/src/index.ts index 09346144..04104e43 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,7 +12,7 @@ * limitations under the License. */ -import { registerOverlay, registerIndicator } from 'klinecharts' +import { registerOverlay, registerIndicator, registerFigure } from 'klinecharts' import overlays from './extension' import chartTypes from './chartType' @@ -27,6 +27,9 @@ import { Datafeed, SymbolInfo, Period, DatafeedSubscribeCallback, ChartProOption import './index.less' import tradeVisualization from './indicator/trade/tradeVisualization' +import rectText from './extension/rectText' + +registerFigure(rectText) overlays.forEach(o => { registerOverlay(o) }) chartTypes.forEach(ct => { registerIndicator(ct) }) From 5638781ed61bd4b69e106a42000a457d5b317295 Mon Sep 17 00:00:00 2001 From: Princelion33 Date: Thu, 11 Jun 2026 21:31:33 +0200 Subject: [PATCH 2/3] fix: resolve compilation errors by adding checkEventOn and installing react types --- package.json | 1 + src/extension/rectText.ts | 3 +++ 2 files changed, 4 insertions(+) diff --git a/package.json b/package.json index 5fe925b4..bc93792b 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ }, "devDependencies": { "@solidjs/testing-library": "^0.8.0", + "@types/react": "^18.2.0", "@typescript-eslint/eslint-plugin": "^5.54.0", "babel-preset-solid": "^1.9.0", "dts-bundle-generator": "^9.5.0", diff --git a/src/extension/rectText.ts b/src/extension/rectText.ts index c92d145a..ebec64a7 100644 --- a/src/extension/rectText.ts +++ b/src/extension/rectText.ts @@ -26,6 +26,9 @@ export interface RectTextStyle { const rectText: FigureTemplate = { name: 'rectText', + checkEventOn: () => { + return false + }, draw: (ctx: CanvasRenderingContext2D, attrs: RectTextAttrs, styles: RectTextStyle) => { try { if (!ctx || !attrs) return From 227e5e1679ad28a1b78e6edd13fe6b18882e5782 Mon Sep 17 00:00:00 2001 From: Princelion33 Date: Thu, 11 Jun 2026 21:38:43 +0200 Subject: [PATCH 3/3] fix(figure): implement bounding box event checking for rectText to enable dragging and clicking --- src/extension/rectText.ts | 51 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/src/extension/rectText.ts b/src/extension/rectText.ts index ebec64a7..494e7fdd 100644 --- a/src/extension/rectText.ts +++ b/src/extension/rectText.ts @@ -26,8 +26,55 @@ export interface RectTextStyle { const rectText: FigureTemplate = { name: 'rectText', - checkEventOn: () => { - return false + checkEventOn: (coordinate: any, attrs: RectTextAttrs, styles: RectTextStyle) => { + try { + if (!coordinate || !attrs) return false + const { x, y, text, align = 'center', baseline = 'middle' } = attrs + if (typeof x !== 'number' || typeof y !== 'number' || text === undefined || text === null) return false + + const textStr = String(text) + const safeStyles = styles || {} + const fontSize = Number(safeStyles.size) || 12 + + const textWidth = textStr.length * (fontSize * 0.6) + const textHeight = fontSize + + const paddingLeft = Number(safeStyles.paddingLeft) ?? 8 + const paddingRight = Number(safeStyles.paddingRight) ?? 8 + const paddingTop = Number(safeStyles.paddingTop) ?? 4 + const paddingBottom = Number(safeStyles.paddingBottom) ?? 4 + + const rectWidth = textWidth + paddingLeft + paddingRight + const rectHeight = textHeight + paddingTop + paddingBottom + + let rectX: number + if (align === 'left') { + rectX = x + } else if (align === 'right') { + rectX = x - rectWidth + } else { // center + rectX = x - rectWidth / 2 + } + + let rectY: number + if (baseline === 'top') { + rectY = y + } else if (baseline === 'bottom') { + rectY = y - rectHeight + } else { // middle + rectY = y - rectHeight / 2 + } + + return ( + coordinate.x >= rectX && + coordinate.x <= rectX + rectWidth && + coordinate.y >= rectY && + coordinate.y <= rectY + rectHeight + ) + } catch (err) { + console.error("Error in checkEventOn:", err) + return false + } }, draw: (ctx: CanvasRenderingContext2D, attrs: RectTextAttrs, styles: RectTextStyle) => { try {