Skip to content

fix: skip default select when interactions already registers element-select - #4696

Open
dyk1454683243-sudo wants to merge 6 commits into
VisActor:developfrom
dyk1454683243-sudo:cursor/fix-interactions-ismultiple-default-select-4563-e3ac
Open

dyk1454683243-sudo wants to merge 6 commits into
VisActor:developfrom
dyk1454683243-sudo:cursor/fix-interactions-ismultiple-default-select-4563-e3ac

Conversation

@dyk1454683243-sudo

@dyk1454683243-sudo dyk1454683243-sudo commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #4563

🤔 This is a ...

  • Bug fix
  • Test Case

🔗 Related issue link

Fixes #4563

💡 Background and solution

Specs that follow the official pie multi-select example:

interactions: [{ type: 'element-select', isMultiple: true }]

could not keep more than one sector selected.

Default select.enable: true still registered a single-select element-select trigger (pointertap, replace). The spec interactions entry registered a second one (click, accumulate). Those configs are not isEqual, so BaseChart._initInteractions kept both live. On click, default select applied selected first; the multi-select trigger then saw hasState and did nothing.

This change skips the default select trigger when spec.interactions already contains an element-select entry. Hover / dimension-hover defaults are unchanged.

  • Docs-shaped interactions multi-select works without select: false
  • Plain default select (no interactions) stays single-select
  • select: { mode: 'multiple' } without interactions still accumulates

📝 Changelog

Language Changelog
🇺🇸 English fix: skip default single-select when interactions already registers element-select, so isMultiple multi-select works
🇨🇳 Chinese 修复:当 interactions 已配置 element-select 时不再注册默认单选,使 isMultiple 多选生效

☑️ Self-Check before Merge

  • Doc is updated/provided or not needed
  • Demo is updated/provided or not needed
  • TypeScript definition is updated/provided or not needed
  • Changelog is provided or not needed

Verification

  • eslint --quiet on packages/vchart/src/series/base/base-series.ts and the new test: clean
  • prettier --check on the same files: clean
  • jest __tests__/unit/interaction/element-select.test.ts --runner=jest-runner --testEnvironment=node: 3 passed
  • Package jest-electron runner is the default; the node runner is enough for this non-render wiring/state regression.

…select

When spec.interactions includes element-select (including isMultiple: true),
do not also register the default single-select trigger so the two do not fight.

Co-authored-by: David <dyk1454683243-sudo@users.noreply.github.com>
Co-authored-by: David <dyk1454683243-sudo@users.noreply.github.com>
@dyk1454683243-sudo
dyk1454683243-sudo marked this pull request as ready for review September 20, 2026 02:33

@xile611 xile611 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dyk1454683243-sudo 已完成 review。这个改动确实修复了饼图 interactions: [{ type: 'element-select', isMultiple: true }] 与默认单选冲突的问题,但目前会关闭自定义交互作用范围以外的默认选中,建议修复这一回归后再合并。

验证基线 c4191139a340f18f0e32dcb353c51009d88a6205 与 PR 312350584a389c56dbfc6010f6cdceb8591509b0:

场景 基线 PR
饼图连续点击两个扇区(配置 selected 样式) 仅保留一个选中 正确多选
柱线组合图仅对 bar 配置 element-select,点击折线 point 正常选中 无法选中
折线图仅对 line 配置 element-select,点击 point 正常选中 无法选中

本地补充用例通过真实 VChart 创建图元,在 Node + canvas 环境显式配置 select: { trigger: 'pointertap' },通过已注册的事件处理器依次发送 pointertap / click;没有手动修改系列字段或替换图元。未做浏览器鼠标实测。PR 自带 3 个测试及修改文件的 ESLint、Prettier 均通过,但未覆盖限定 markNames 的场景。

请在解析实际匹配的图元后处理冲突,保留未被自定义交互覆盖的图元的默认选中;同时补充上述局部图元、跨系列场景的回归测试。具体位置见行内意见。

Comment on lines +960 to +963
if (interactions?.some(interaction => interaction.type === TRIGGER_TYPE_ENUM.ELEMENT_SELECT)) {
const defaultSelectIndex = res.findIndex(item => item.trigger.type === TRIGGER_TYPE_ENUM.ELEMENT_SELECT);
if (defaultSelectIndex >= 0) {
res.splice(defaultSelectIndex, 1);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 保留自定义交互作用范围外的默认选中

这里只要存在 element-select 就删除整个默认 select,忽略了 markNames / markIds 的作用范围。例如柱线组合图在图表级配置 interactions: [{ type: 'element-select', markNames: ['bar'], isMultiple: true }]:该配置会传给两个系列,折线系列虽然没有匹配的 bar 图元,也会先删除默认 select,随后又因匹配结果为空而不注册自定义交互,最终折线点完全无法选中。单个折线图配置 markNames: ['line'] 时,未被覆盖的 point 同样失去选中能力。上述两个场景均已验证基线通过、PR 失败。请先解析实际匹配的图元,仅处理重叠范围内的冲突,保留其他图元的默认选中,并补充回归测试。

@visactor-review-bot visactor-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已核对冻结 diff、默认与自定义 element-select 的注册及状态更新路径,新增发现 1 项独立的反选样式回归。既有作用范围缺陷仍待解决,参见 https://github.com/VisActor/VChart/pull/4696#discussion_r4056020786;本次不重复提交该问题。

验证:静态核验:默认 select 配置 reverseState='selected_reverse';本次改动移除该触发器;自定义 ElementSelect 默认选项未补 reverseState;Interaction.updateStates 因而绕开反选状态分支。依赖未安装,未运行自动化或视觉测试,不能视为测试通过。

限制:隔离环境未安装仓库依赖且无网络,未运行 Jest、浏览器交互或视觉回归;结论基于冻结源码的交互配置和状态更新调用链静态核对。;未对基线和 PR 进行可执行渲染对照;既有 review 所述局部图元与跨系列回归仅引用既有讨论,不作为本次新发现。

状态:completed

if (interactions?.some(interaction => interaction.type === TRIGGER_TYPE_ENUM.ELEMENT_SELECT)) {
const defaultSelectIndex = res.findIndex(item => item.trigger.type === TRIGGER_TYPE_ENUM.ELEMENT_SELECT);
if (defaultSelectIndex >= 0) {
res.splice(defaultSelectIndex, 1);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 替换默认选中时保留 selected_reverse 状态

当系列配置 state.selected_reverse,同时使用文档形式的 interactions: [{ type: 'element-select', isMultiple: true }](未显式填写 reverseState)时,删除默认 select 后,未选中的图元不再进入 selected_reverse,原本的淡化等样式消失。原因是 _defaultSelectConfig 为默认触发器设置了 reverseState: STATE_SELECTED_REVERSE,而自定义触发器直接使用原始 spec;ElementSelect.defaultOptions 仅补 state 和 trigger,Interaction.updateStates 收到空的 reverseState 时只执行选中分支。此前默认触发器会设置未选中图元的反选状态;且 IElementSelectSpec 的注释明确允许用 state.selected_reverse 配置非激活图元。建议在避免双触发器冲突的同时为未显式覆盖该字段的自定义 select 保留默认反选状态,并增加该样式的真实状态回归测试。此问题与现有讨论所述 markNames / markIds 导致图元无法选中的作用范围问题不同:这里即使自定义交互覆盖全部图元,反选视觉状态也会丢失。

Keep default select for marks outside a custom element-select, and copy
selected_reverse onto that trigger when it replaces the default.

Co-authored-by: David <dyk1454683243-sudo@users.noreply.github.com>

@visactor-review-bot visactor-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

发现 1 个需要修复的新问题,见行内评论。

});

if (defaultSelect && coveredDefaultSelectMarkIds.size) {
defaultSelect.marks = defaultSelect.marks.filter(mark => !coveredDefaultSelectMarkIds.has(mark.id));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 保留局部自定义选中时其他图元的跨图元反选样式

当同一折线系列的 line、point 都配置 selected_reverse,且 interactions 只对 line 配置 element-select 时,这里把 line 从默认 select 的 marks 中剔除。点击 point 后,默认触发器虽仍能选中 point,但 Interaction.addBothStateOfGraphics 只遍历该触发器的 marks,因而不再给未选中的 line 图元添加 selected_reverse;反过来点击 line 也不会反选 point。基线的默认触发器包含两种图元,可正确将另一种图元置为反选态。这与既有讨论中“point 完全无法选中”的问题不同:现在 point 可选中,但跨图元的反选视觉效果丢失。建议分离默认选中的事件覆盖范围与反选状态传播范围,或在共享状态的触发器中保留对未选图元的反选处理,并补充点击一种图元后检查另一种图元真实状态的用例。已用独立 Node 简化复现验证 marks 范围差异,未运行工程交互或视觉测试。

When a custom element-select covers only some default-select marks, keep
click handling split but still apply selected_reverse across the original
mark set.

Co-authored-by: David <dyk1454683243-sudo@users.noreply.github.com>

@visactor-review-bot visactor-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

发现 1 个需要修复的新问题,见行内评论。

const eventMarkIds = new Set(customSelect.marks.map(mark => mark.id));
const reverseMarks = originalMarks.filter(mark => !eventMarkIds.has(mark.id));
if (reverseMarks.length) {
customSelect.reverseMarks = reverseMarks;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 拆分选中触发器后需同步跨 mark 的选中状态

当折线系列的 interactions 仅对 line 配置 element-select、而默认 select 仍负责 point,且两者都配置 selected / selected_reverse 样式时,先点击 point 再点击 line:point 默认触发器已将 point 设为 selected、line 设为 selected_reverse;这里让 line 自定义触发器又将 point 纳入反选范围,但两个触发器分别保存 statedGraphics。第二次点击时 addBothStateOfGraphics 对 point 追加 selected_reverse,不会移除其原有 selected,结果 point 同时为选中和反选、line 也保持选中,单选视觉及状态不一致。此前默认触发器覆盖两个 mark,第二次点击会清除 point 的 selected。建议在两个触发器之间协调已选图元的状态/清理,补充“先点 point、再点 line、再取消”的连续交互回归测试;目前仅以源码和简化 Node 状态序列核验,未运行真实 VChart 交互测试。

Co-authored-by: David <dyk1454683243-sudo@users.noreply.github.com>

@visactor-review-bot visactor-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

发现 1 个需要修复的新问题,见行内评论。

const markId = graphic.context?.markId;
const hasAnimation = this._hasAnimationByGraphicState(graphic, markById);
if (graphicHasState(graphic, state)) {
removeGraphicState(graphic, state, hasAnimation);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 保留 setSelected 跨拆分图元的批量选中

当折线系列仅对 line 配置 interactions: [{ type: 'element-select', markNames: ['line'] }],再用公开的 setSelected(datumArray) 同时选中 line 与 point 图元时,updateStateOfGraphics 会依次将同一批图元交给拆分后的默认触发器与自定义触发器;第二个触发器在这里把第一个触发器刚设好的 selected 移除并清空其选中记录,随后 addBothStateOfGraphics 还会将其置为 selected_reverse。最终只能留下后处理的那一种图元选中,调用者指定的另一种图元丢失;此前默认触发器同时覆盖两种图元,批量调用可以保留两者。建议对程序化批量设置按整批目标协调拆分触发器,而不是逐个触发器互斥释放;增加同时命中 line/point 的 setSelected 状态断言。此结论是冻结源码调用链推导,当前环境未运行工程测试。

Programmatic setSelected that hits both line and point was applied one
split trigger at a time, so the second trigger cleared the first mark and
put it into selected_reverse. Treat that batch as one target set.

Co-authored-by: David <dyk1454683243-sudo@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] isMultiple is not working in the interactions configuration.

3 participants