[proposal] Switch constrained drag-to-value policy (design-only)
Nobody has claimed this yet.
- 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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