Proto-UI / Proto-UI/Proto-UI

[proposal] Switch constrained drag-to-value policy (design-only)

Open
#498 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

advanced contribution area: prototypes area: spec needs semantic decision
Dominant language
TypeScript
Stars
35
Forks
15
Avg merge
2d 23h
Merged PRs (30d)
107

Description

Design proposal from #495(2026-08-24)。Non-normative:本提案不替代 spec/** authority,但可直接驱动 draft entity、可逆实现与 executable evidence。只有 identity、owner、public guarantee 或兼容性仍无权威答案时,才把该具体选择标为 needs semantic decision。镜像记录:internal/records/2026-08-24-switch-drag-to-value-proposal.zh-CN.md

问题与定位

当前 Switch 只支持点击翻转:root 经 press.commit 一次性取反 checked,thumb 是二态 CSS 切换。拖动时 thumb 不跟手。固定基线同样点击式,现状不是 drift;本 proposal 回答 checkpoint 问题:在不改既有负边界的前提下,"thumb 跟手拖动、释放按阈值提交"的信息通路、仲裁规则与证据如何构成,并让 Switch 承担 C-MOVE-GESTURE-0001 第二消费者验证。

Authority map

  • C-MOVE-GESTURE-0001 + HC-MOVE-GESTURE-0001:有界 move session(start / 有序 samples / 恰好一次 end-or-cancel;五种 cancel reason)。契约显式排除 DnD、领域 value、a11y widget semantics。
  • 现有唯一消费者 Scroll:immediate activation、无 commit 语义、无外部 truth owner——正是 Switch 要补验的空白。
  • Root 独占 checked/activation/commit(P-BASE-SWITCH-*);Thumb 零 ownership(absence-as-implementation)。
  • Controlled:仅 emit checkedChange 不本地落位。

信息通路(candidate)

pointer.down on track/root(disabled 抑制,维持 pressed 视觉)
  → 绑定 MoveGesture session(axis: horizontal, activation: threshold)
    → 未达阈值:等同今天的普通按压
    → 达到阈值:activated
        → provisional position = clamp(sample.x − trackRect.left − inset, [0, travel])
          (trackRect 会话开始测一次,host-local;
            输出中性 progress ∈ [0,1],经样式投影缝投射为临时 paint output)
    → end:位置+速度阈值 → requestedChecked
        → 走既有单一请求通道(emit checkedChange / uncontrolled 本地落位)
    → cancel(任一 C-MOVE-GESTURE cancel reason):零请求;thumb 回当前 truth

六个需要由 authority 与证据收敛的重点

1. Press/tap 仲裁

Root 保持唯一 activation owner。drag policy 在既有 pressed 生命周期上观察 samples,达阈值后声明"升级为 drag"并抑制本次手势的 press.commit 翻转;接合点是请求通道与 pressed 视觉,不开第二个 pointer 监听面。纯 tap 走现行路径不进 drag 决策。

2. Activation threshold

候选:沿轨道轴位移 ≥ host 中立常量(如 4px 等效)即激活;值为 policy 常量非 author prop。阈值前后唯一可观测差异是 thumb 是否跟手,pressed 视觉两态皆保持。

3. Provisional host-local position

track 几何会话开始测量一次;输出中性 progress 经样式投影缝(CSS 自定义属性 / data attribute,具体归 Adapter)成为临时 paint output。thumb 端点位姿仍由 checked 驱动,两者不打架:checked truth 未变,端点样式不切换。不写 context/anatomy/State,Thumb 零 ownership 原样成立。

4. Commit/cancel

End 决策(candidate):progress > 0.5 或释放速度明确越界 → 对应端点;否则回拖动前 checked。恰好一次提交,复用 press.commit 现行双分支语义(uncontrolled set / controlled emit)。五种 cancel reason 全部零请求回 truth。Escape 取消列为可选增强。

5. Controlled truth 回退

owner 拒绝(checked prop 未变)→ thumb 回当前 truth,永不乐观本地落位;回退动画与取消相同。这正是 Scroll 无法提供的"外部 truth owner"验证。

6. 单次 activation

不变量:每次手势至多一个 toggle 请求。tap → 恰好一个;drag → 至多一个(end 时);cancel → 零个。升级 drag 后必须吞掉底层翻转防双重 toggle。列为首要单测断言。

键盘与 a11y(不变量)

Space/Enter、focus、aria-checked 全部不动。拖动是纯指针增强。所有新逻辑都在 root 内部或其 policy hook,Thumb 仍 presentational——负边界自检通过。

载体形态(candidate)

倾向 base switch 域内专用 asHook(如 useSwitchDragPolicy,消费 HC-MOVE-GESTURE cap),不建通用 module/API。理由:#495 的负边界明确禁止预设通用原语;第二消费者的职责是验证 substrate 够不够用,而非立刻抽象它。

跨宿主与 fallback

MoveGesture 已 host-mediated(web-move-gesture-host 三 runtime 注册)。policy 只消费中立 samples。无手势宿主能力 → fallback 到今天完全一致的点击式(策略整体 opt-in)。不假设 scrollTop 类 Web 特权。

可执行证据与 falsification

  • 首要不变量单测:tap 单次翻转;drag 到底恰一次请求且无额外翻转;五种 cancel reason 零请求回 truth。
  • threshold 边界:恰等阈值的判定二选一后锁死测试。
  • controlled:接受 → 新 truth;拒绝 → 回旧 truth 无中间落位。
  • Browser journey(三 runtime):拖动连续跟手断言;释放落位;中途取消回弹;键盘 Space 行为不变。
  • falsify「Move substrate 充分性」:若 policy 必须绕过/扩展 C-MOVE-GESTURE-0001 任何保证(多指、hover 采样、session 内 geometry 变更),即为 substrate 缺口证据,回报 Move 契约修订而非域内 hack。
  • 体感校准类只能实现后定:阈值像素、速度曲线;iOS 式 overshoot 首轮显式 out of scope。

下一步

直接建立一个有界 implementation/evidence Issue,先验证单次 activation、controlled truth 回退与 Move substrate 充分性。当前提案不直接激活稳定公共 API 或 entity;若实验证据仍留下具体产品方向分叉,只暂停该选择,其余实现与证据继续推进。

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with this proposal, the mirrored record at internal/records/2026-08-24-switch-drag-to-value-proposal.zh-CN.md, and the authority referenced in spec/** and #495. Inspect the Switch root press.commit path, C-MOVE-GESTURE-0001, and the proposed useSwitchDragPolicy entry point. Done means a separate bounded implementation/evidence issue resolves the listed decisions and defines tests for single activation, controlled truth fallback, cancellation, and substrate sufficiency.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
design, frontend
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.