arthub-table 0.2.60 → 0.2.61-next.0

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.
@@ -26,6 +26,10 @@ export interface PreprocessedRow {
26
26
  _searchMatch: SearchMatchResult;
27
27
  _cellBgColors: Record<string, string>;
28
28
  _bgColor?: string;
29
+ /** CP 行高亮标记 */
30
+ _cpHighLight?: boolean;
31
+ /** CP 高亮行中需要使用高亮文字色的列 key */
32
+ _cpHighLightFields?: string[];
29
33
  _isTopLevelEntity?: boolean;
30
34
  _isEmpty?: boolean;
31
35
  _isGroup?: boolean;
@@ -47,7 +47,7 @@ declare class AddTaskRow extends Context {
47
47
  */
48
48
  click(x: number, y: number, _clientX?: number, _clientY?: number): void;
49
49
  mouseDown(_x: number, _y: number): void;
50
- dbClick(_x: number, _y: number): void;
50
+ dbClick(_x: number, _y: number, _clientX?: number, _clientY?: number): void;
51
51
  rePaint(): void;
52
52
  resizeColumn(_colIndex: number, _diffWidth: number): void;
53
53
  checked: boolean;
@@ -70,9 +70,9 @@ declare class Body {
70
70
  handleCheckRow(y?: number): void;
71
71
  handleCheckHeader(): void;
72
72
  mouseMove(x: number, y: number): void;
73
- mouseDown(x: number, y: number, shiftKey?: boolean): void;
73
+ mouseDown(x: number, y: number, shiftKey?: boolean, clientX?: number, clientY?: number): void;
74
74
  click(x: number, y: number, clientX?: number, clientY?: number, event?: MouseEvent): void;
75
- dbClick(x: number, y: number): void;
75
+ dbClick(x: number, y: number, clientX?: number, clientY?: number): void;
76
76
  /**
77
77
  * Check if double click hits a viewer with onDblClick handler
78
78
  * @returns true if viewer handled the double click
@@ -23,6 +23,10 @@ declare class GroupRow extends Context {
23
23
  isArrowAreaHovered: boolean;
24
24
  /** 人员工时图标的边界矩形缓存,用于点击和悬停命中检测 */
25
25
  private userWorkingHoursIconRect;
26
+ /** IText 实例,用于组头文字选中复制(非 person 模式) */
27
+ private _groupNameIText;
28
+ /** IText 实例数组,person 模式下每个非空人员一个实例 */
29
+ private _groupNameITexts;
26
30
  constructor(grid: DataGrid, rowIndex: number, x: number, y: number, height: number, data: GroupedTableRowData);
27
31
  /**
28
32
  * Draw the group row
@@ -121,6 +125,16 @@ declare class GroupRow extends Context {
121
125
  * 参考旧表格 GroupHeaderCell.updateMiddle 中根据字段 editor 类型分派渲染的逻辑
122
126
  */
123
127
  private drawGroupNameWithViewer;
128
+ /**
129
+ * person 模式:直接复用 viewer._lastPersonLayout 的布局结果创建/复用 IText 实例。
130
+ * 与 _drawTagITexts 模式一致:viewer 负责绘制,IText 只更新 bounds 供命中检测和选区使用。
131
+ */
132
+ private _drawPersonITexts;
133
+ /**
134
+ * tag 模式:为每个 tag 创建/复用 IText 实例,接管文字渲染(viewer 只画背景)。
135
+ * 直接复用 viewer.draw() 已计算好的 _lastTagLayout,确保文字与背景精确对齐,避免重复计算。
136
+ */
137
+ private _drawTagITexts;
124
138
  /**
125
139
  * 估算 viewer 绘制后的宽度,用于定位后续元素(如 count badge)
126
140
  * 针对不同 viewer 类型的绘制逻辑进行精确估算,避免内联统计值与 viewer 内容重叠
@@ -200,7 +214,7 @@ declare class GroupRow extends Context {
200
214
  */
201
215
  private drawGroupHeaderCells;
202
216
  /**
203
- * 获取列在屏幕上的实际 X 坐标
217
+ * 绘制分组行统计数据(第二行) * 获取列在屏幕上的实际 X 坐标
204
218
  */
205
219
  private getColumnScreenX;
206
220
  /**
@@ -300,7 +314,7 @@ declare class GroupRow extends Context {
300
314
  handleStartAutofill(_x: number, _y: number, _shiftKey?: boolean): void;
301
315
  isInsideVerticaTableBoundary(mouseX: number, mouseY: number): boolean;
302
316
  mouseMove(x: number, y: number): void;
303
- mouseDown(_x: number, _y: number): void;
317
+ mouseDown(x: number, y: number, clientX?: number, clientY?: number): void;
304
318
  click(x: number, y: number, clientX?: number, clientY?: number): void;
305
319
  /** 处理 nested 子列点击 */
306
320
  private onNestedChildClick;
@@ -315,7 +329,8 @@ declare class GroupRow extends Context {
315
329
  * 而 ColumnHeader 的 index 是叶子列序号,与 headers 树形结构无对应关系。
316
330
  */
317
331
  private findColumnConfig;
318
- dbClick(_x: number, _y: number): void;
332
+ dispose(): void;
333
+ dbClick(x: number, y: number, clientX?: number, clientY?: number): void;
319
334
  rowHeader: {
320
335
  handleCheck: (_val: boolean) => void;
321
336
  };
@@ -0,0 +1,118 @@
1
+ /**
2
+ * IText - 独立 Canvas 文字渲染组件
3
+ * 支持:点击定位光标、鼠标拖拽选区、双击选词、Ctrl/Cmd+C 复制、Ctrl/Cmd+A 全选
4
+ * 不依赖 Viewer 体系,可在任何 Canvas 渲染场景中使用(组头、普通单元格等)
5
+ */
6
+ export interface ITextOptions {
7
+ /** 字体大小,默认 12 */
8
+ fontSize?: number;
9
+ /** 字体粗细,默认 'normal' */
10
+ fontWeight?: 'normal' | 'bold';
11
+ /** 字体族,默认 sans-serif */
12
+ fontFamily?: string;
13
+ /** 文字颜色,默认 '#333' */
14
+ color?: string;
15
+ /** 文字对齐方式,默认 'left' */
16
+ textAlign?: 'left' | 'center' | 'right';
17
+ /** 内边距,默认 4 */
18
+ padding?: number;
19
+ /** requestRedraw 回调,激活后每次选区变化时调用 */
20
+ requestRedraw?: () => void;
21
+ /**
22
+ * 选区样式:
23
+ * - 'solid':蓝底白字(纯文本场景,默认)
24
+ * - 'overlay':半透明蓝色覆盖(viewer 场景,不会遮住 viewer 内容)
25
+ */
26
+ selectionStyle?: 'solid' | 'overlay';
27
+ }
28
+ export declare class IText {
29
+ private text;
30
+ private options;
31
+ /** 最后一次 draw 时的 canvas 绝对坐标 */
32
+ private x;
33
+ private y;
34
+ private width;
35
+ private height;
36
+ private selectionStart;
37
+ private selectionEnd;
38
+ private isActiveState;
39
+ private isDragging;
40
+ private anchorIndex;
41
+ private charWidths;
42
+ private charPositions;
43
+ private textarea;
44
+ private cleanupMouseMove;
45
+ private cleanupMouseUp;
46
+ private onKeydown;
47
+ /** charWidths 是否需要重新计算(文本变化时置为 true) */
48
+ private charsDirty;
49
+ constructor(text: string, options?: ITextOptions);
50
+ /** 更新文本(文本变化时调用) */
51
+ setText(text: string): void;
52
+ /** 更新文字颜色(每帧 draw 前可调用,适用于颜色来源不同的场景) */
53
+ setColor(color: string): void;
54
+ /**
55
+ * 批量更新选项(fontSize、fontWeight、fontFamily、color 等)
56
+ * viewer 路径每帧同步样式时使用
57
+ */
58
+ setOptions(opts: Partial<ITextOptions>): void;
59
+ /** 更新 requestRedraw 回调 */
60
+ setRequestRedraw(fn: () => void): void;
61
+ /** 判断 canvas 绝对坐标是否在文字区域内,左侧留 4px buffer 方便从文字起始位置附近点击激活 */
62
+ containsPoint(canvasX: number, canvasY: number): boolean;
63
+ /**
64
+ * 渲染文字 + 选区高亮(每帧调用)
65
+ * x, y, width, height 是文字区域在 canvas 上的绝对坐标
66
+ *
67
+ * drawText 参数语义:
68
+ * - true(默认):IText 绘制文字 + 选区,外部无需再绘制文字
69
+ * - false:IText 只更新 bounds(供命中检测使用),不绘制任何内容
70
+ * 适用于 viewer 路径中非 text 模式(viewer 自己负责绘制文字)
71
+ */
72
+ draw(ctx: CanvasRenderingContext2D, x: number, y: number, width: number, height: number, drawText?: boolean): void;
73
+ /**
74
+ * 鼠标按下:激活 + 定位光标 + 开始拖拽
75
+ * clientX/clientY 是鼠标的视口坐标
76
+ */
77
+ handleMouseDown(canvasEl: HTMLCanvasElement, clientX: number, _clientY: number): void;
78
+ /**
79
+ * 鼠标移动:更新拖拽选区(仅在 isDragging 时有效)
80
+ * 内部由 mousemove 监听器调用
81
+ */
82
+ handleMouseMove(canvasEl: HTMLCanvasElement, clientX: number, _clientY: number): void;
83
+ /** 鼠标抬起:结束拖拽 */
84
+ handleMouseUp(): void;
85
+ /** 双击:全选所有文字 */
86
+ handleDblClick(canvasEl: HTMLCanvasElement, clientX: number, clientY: number): void;
87
+ /** 失活:清除选区、销毁 textarea */
88
+ deactivate(): void;
89
+ /** 是否处于激活状态 */
90
+ get active(): boolean;
91
+ /**
92
+ * 单独绘制选区覆盖层(在所有内容绘制完成后调用)
93
+ * 用于 viewer 路径:viewer 先绘制内容,最后再叠加选区高亮
94
+ */
95
+ drawSelectionOverlay(ctx: CanvasRenderingContext2D): void;
96
+ /** 获取当前选中文字(限制在可见区域内) */
97
+ getSelectedText(): string;
98
+ /** 获取可见区域内最后一个完整字符的索引上限 */
99
+ private getMaxVisibleIndex;
100
+ private buildFont;
101
+ private measureCharWidths;
102
+ private getTextStartX;
103
+ private drawSelection;
104
+ /** 分段绘制文字:选中区域白色,其余原色 */
105
+ private drawTextWithSelection;
106
+ /** 将视口坐标 clientX 转为相对于文字起始位置的局部 X */
107
+ private clientToLocalX;
108
+ /** 根据局部 X 坐标找到最近的字符索引,结果限制在可见区域内 */
109
+ private getCharIndexFromX;
110
+ private mountTextarea;
111
+ private unmountTextarea;
112
+ private registerDragListeners;
113
+ }
114
+ /**
115
+ * 取消当前激活的 IText 实例的选区
116
+ * 在 canvas 全局 mousedown 入口调用,模拟 DOM 原生选中行为:点击任意位置即取消选区
117
+ */
118
+ export declare function deactivateActiveIText(): void;
@@ -17,12 +17,12 @@ declare class Row extends Context {
17
17
  constructor(grid: DataGrid, rowIndex: number, x: number, y: number, height: number, data: RowData);
18
18
  isInVerticalAutofill(mouseX: number, mouseY: number): boolean;
19
19
  handleCheck(checked?: boolean, event?: MouseEvent, silent?: boolean): void;
20
- mouseDown(x: number, y: number): void;
20
+ mouseDown(x: number, y: number, _clientX?: number, _clientY?: number): void;
21
21
  mouseMove(mouseX: number, mouseY: number): void;
22
22
  handleAutofill(x: number, y: number): void;
23
23
  handleStartAutofill(x: number, y: number, shiftKey?: boolean): void;
24
24
  click(x: number, y: number, event?: MouseEvent): void;
25
- dbClick(x: number, y: number): void;
25
+ dbClick(x: number, y: number, _clientX?: number, _clientY?: number): void;
26
26
  resizeColumn(colIndex: number, diffWidth: number): void;
27
27
  resizeRow(rowIndex: number, height: number): void;
28
28
  rePaint(): void;
@@ -173,6 +173,10 @@ export interface ThemeTokens {
173
173
  bgTopLevelEntity: string;
174
174
  /** 顶层实体行 hover 背景色 */
175
175
  bgTopLevelEntityHover: string;
176
+ /** CP 高亮行背景色 */
177
+ bgCpHighlight: string;
178
+ /** CP 高亮行文字色 */
179
+ textCpHighlight: string;
176
180
  /** 需求变更-新增需求背景色 */
177
181
  bgDemandChangeAdd: string;
178
182
  /** 需求变更-新增需求 hover 背景色 */
@@ -13,6 +13,27 @@
13
13
  * and this viewer only handles rendering. This keeps arthub-table free of business dependencies.
14
14
  */
15
15
  import type { CellViewer, ViewerRenderContext, GroupNameViewerData } from './types';
16
+ /** tag 模式单个 tag 的布局计算结果 */
17
+ interface TagLayoutItem {
18
+ /** 原始 tag 数据 */
19
+ tag: {
20
+ text: string;
21
+ bgColor: string;
22
+ textColor: string;
23
+ };
24
+ /** tag 背景起始 X(绝对坐标) */
25
+ tagX: number;
26
+ /** tag 背景宽度(可能因截断而缩小) */
27
+ tagWidth: number;
28
+ /** 文字起始 X(tagX + paddingH) */
29
+ textX: number;
30
+ /** 文字可用宽度(tagWidth - 2*paddingH,最小为 0) */
31
+ textWidth: number;
32
+ /** 实际显示文字(可能含省略号) */
33
+ displayText: string;
34
+ /** 是否渲染(false 表示剩余空间不足,跳过该 tag 及后续 tag) */
35
+ visible: boolean;
36
+ }
16
37
  declare class GroupNameViewer implements CellViewer<GroupNameViewerData> {
17
38
  readonly type = "group-name";
18
39
  private imageManager;
@@ -21,6 +42,15 @@ declare class GroupNameViewer implements CellViewer<GroupNameViewerData> {
21
42
  private _hoverPersonIndex;
22
43
  /** Row index of the group row currently being hovered (-1 = none) */
23
44
  private _hoverRowIndex;
45
+ /** 最近一次 drawTagMode 计算出的布局结果,供 _drawTagITexts 复用 */
46
+ _lastTagLayout: TagLayoutItem[];
47
+ /** 最近一次 drawPersonMode 计算出的每个人的布局(nameX, nameWidth, displayName),供 onClick/getInteractiveCursor 复用 */
48
+ _lastPersonLayout: Array<{
49
+ nameX: number;
50
+ nameWidth: number;
51
+ displayName: string;
52
+ personIndex: number;
53
+ }>;
24
54
  private get avatarColors();
25
55
  constructor();
26
56
  /**
@@ -52,6 +82,15 @@ declare class GroupNameViewer implements CellViewer<GroupNameViewerData> {
52
82
  private getColorIndex;
53
83
  private getInitial;
54
84
  private drawTagMode;
85
+ /**
86
+ * 计算 tag 模式的布局,返回每个 tag 的渲染参数。
87
+ * 策略与 TextViewer.drawMultiTags 对齐:
88
+ * - 总宽度 <= 可用宽度 → 各 tag 用理想宽度完整显示
89
+ * - 总宽度 > 可用宽度 → 等比缩小所有 tag(minTagWidth 兜底),确保所有 tag 都显示
90
+ * - 等比缩小后仍超出 → 减少可见 tag 数(极端情况)
91
+ * - 每个 tag 内文字超出 tag 宽度 → 省略号截断
92
+ */
93
+ private _computeTagLayout;
55
94
  private drawBooleanMode;
56
95
  private drawPriorityMode;
57
96
  }
@@ -8,6 +8,17 @@
8
8
  import type { CellViewer, ViewerRenderContext, TaskNodeViewerData } from './types';
9
9
  declare class TaskNodeViewer implements CellViewer<TaskNodeViewerData> {
10
10
  readonly type = "task-node";
11
+ /**
12
+ * draw 时每个 cell 内所有 tag 的几何与"是否截断"信息缓存。
13
+ * 二级结构 WeakMap<scope, Map<`${rowIndex}-${colIndex}`, NameOverflowInfo[]>>:
14
+ * - 外层 scope 区分 DataGrid 与内部 NestedGrid,避免内外层相同 rowIndex/colIndex 互相覆盖
15
+ * - 作用域实例被 GC 后 WeakMap 自动释放,无需主动清理
16
+ *
17
+ * 写入:draw 末尾覆盖式写入
18
+ * 读取:getTooltip 用 localX/localY 命中后查询
19
+ * 清理策略:依赖 draw 自动刷新(每次重绘同 key 覆盖),不主动清理。
20
+ */
21
+ private _nameOverflowCache;
11
22
  private imageManager;
12
23
  /** 记录加载失败的图标 URL,避免反复重试 */
13
24
  private failedIcons;
@@ -112,5 +123,14 @@ declare class TaskNodeViewer implements CellViewer<TaskNodeViewerData> {
112
123
  * 文本截断,超出宽度时显示省略号
113
124
  */
114
125
  private truncateText;
126
+ getTooltip(context: ViewerRenderContext, _data: TaskNodeViewerData, localX: number, localY: number): {
127
+ text: string;
128
+ bounds: {
129
+ x: number;
130
+ y: number;
131
+ width: number;
132
+ height: number;
133
+ };
134
+ } | null;
115
135
  }
116
136
  export default TaskNodeViewer;
@@ -570,6 +570,10 @@ export interface GroupNameViewerData extends CellViewerData {
570
570
  priorityValue?: number | string;
571
571
  /** Callback when a person name is clicked (for user profile jump) */
572
572
  onPersonClick?: (accountName: string) => void;
573
+ /** 跳过文字绘制,由外部 IText 负责绘制文字(text 模式专用) */
574
+ skipText?: boolean;
575
+ /** 可见裁剪宽度,tag 模式用于控制截断逻辑(超出则显示省略号而非被 clip 截断) */
576
+ clipWidth?: number;
573
577
  }
574
578
  /**
575
579
  * Separator row viewer data
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arthub-table",
3
- "version": "0.2.60",
3
+ "version": "0.2.61-next.0",
4
4
  "description": "High-performance canvas-based table/grid component for Vue 3 with TypeScript support, featuring virtual scrolling, cell viewers, grouped rows, and nested grids.",
5
5
  "main": "dist/arthub-table.common.js",
6
6
  "module": "dist/arthub-table.umd.min.js",