diff --git a/.ua/backlog.json b/.ua/backlog.json index 2d42ebd..069b087 100644 --- a/.ua/backlog.json +++ b/.ua/backlog.json @@ -3122,6 +3122,15 @@ "motivation": "sortableList 的拖拽态原本整行 opacity: .7,实测把拖拽行正文对比从 12.03 压到 4.86(亮色)——低于项目正文 7:1 的口径,而「正在被拿起」完全可以用虚线边 + 抬升投影表达,不必牺牲可读性。", "acceptance": "① 拖拽态去掉整行 opacity,改用 --ua-shadow-2 抬升 + 虚线 accent 边 + tint-accent 底;② 静态态对比回到 12+(亮)/ 11+(暗);③ 把手悬停转 accent 色提示可抓,拖拽中 cursor 为 grabbing;④ audit:visual / audit:contrast / audit:motion 全绿,manifest 步骤同步。", "notes": ".is-dragging 去 opacity 改 --ua-shadow-2 抬升 + 虚线 accent 边 + tint-accent 底(对比 4.86 → 12.03 亮 / 11.32 暗);把手悬停转 accent、拖拽中 grabbing;item 过渡补 box-shadow,manifest 步骤同步" + }, + { + "id": "g304", + "domain": "tooling", + "status": "open", + "title": "配方补一条可排序检查单:句柄式组件的拼装样例", + "motivation": "sortableList 是句柄式组件(返回 { el, move, order }),与 form / virtualList 同类,但 18 条配方里没有一条演示句柄式拼装——Agent 照配方抄会把返回值直接当节点 append,页面一片空白;「顺序只存一份」「move 与拖拽同一条提交路径」这些约定也没人写下来。", + "acceptance": "① recipes.md 新增配方 19(编号连续、带代码块与要点),代码块里认出 sortableList / card / button / toast;② 要点覆盖句柄要 append(.el)、顺序唯一真相、move 与拖拽同路径、边缘不自动滚动、条目文案唯一;③ ua recipes 19 能取到该条,llms-full.txt 与 llms.txt 重生成;④ npm run verify / check 全绿。", + "notes": "" } ] } diff --git a/llms-full.txt b/llms-full.txt index 99bd30d..0486d5d 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -3264,6 +3264,30 @@ const panel = h('div', { class: 'review' }, - `codeBlock` 的行号不参与复制,`copyButton` 直接读原文,所以「复制出来的代码」永远干净。 - 差异行多了给 `diffView` 外层限定高度(或放进抽屉),否则长差异会把审阅按钮挤出视口。 +## 配方 19:可排序的检查单(拖拽 + 键盘搬位 + 顺序回写) + +```ts +const steps = ['需求评审', '接口联调', '灰度发布', '线上回归']; +const order = signal(steps); // 顺序唯一真相:组件与下方摘要读同一个信号 +const list = sortableList({ + items: steps, // 文案即排序键,要求唯一 + value: order, + onChange: (next) => toast.success(`已调整:${next[0]} 置顶`), +}); +card({ title: '发布检查单(可拖动,Alt+↑↓ 键盘搬位)' }, + list.el, // 返回的是句柄不是节点:忘了 append(el) 页面上一片空白 + h('p', { class: 'desc' }, () => order().map((t, i) => `${i + 1}. ${t}`).join(' ')), + button({ label: '把「灰度发布」置顶', variant: 'ghost', onClick: () => list.move(2, 0) }), +); +``` + +要点: +- `sortableList` 返回 `{ el, move, order }` 句柄(与 `form` / `virtualList` 同类):节点在 `el` 上,业务侧要改顺序用 `move(from, to)`,读当前顺序用 `order()`。 +- 顺序只存一份:`value` 信号同时喂组件与摘要 / 上报逻辑,否则会出现「界面拖了、提交的仍是老顺序」。 +- `move()` 与拖拽走同一条提交路径(都会触发 `onChange`),所以「置顶 / 上移」按钮不必另写状态同步;越界下标静默忽略,不用自己判边界。 +- 拖到边缘不会自动滚动:条目多到一屏放不下时改用筛选或上下移按钮,不要指望拖出视口;跨列表搬运用两个实例各持一个信号。 +- 条目文案要唯一——重名会让「谁被搬走了」失去参照,需要重复内容时把序号拼进文案(`'确认条款 #1'`)。 + ## 通用约定 - 状态一律放信号(`signal` / `computed`),组件只读写信号,不持有业务数据副本。 diff --git a/llms.txt b/llms.txt index d0eeae1..5bd12e8 100644 --- a/llms.txt +++ b/llms.txt @@ -190,3 +190,4 @@ - 配方 16:后台页骨架(3 条要点) - 配方 17:表格刷新遮罩(3 条要点) - 配方 18:变更审阅面板(3 条要点) +- 配方 19:可排序的检查单(拖拽 + 键盘搬位 + 顺序回写)(5 条要点) diff --git a/packages/tools/cli/recipes.md b/packages/tools/cli/recipes.md index 065e64e..b0305a7 100644 --- a/packages/tools/cli/recipes.md +++ b/packages/tools/cli/recipes.md @@ -407,6 +407,30 @@ const panel = h('div', { class: 'review' }, - `codeBlock` 的行号不参与复制,`copyButton` 直接读原文,所以「复制出来的代码」永远干净。 - 差异行多了给 `diffView` 外层限定高度(或放进抽屉),否则长差异会把审阅按钮挤出视口。 +## 配方 19:可排序的检查单(拖拽 + 键盘搬位 + 顺序回写) + +```ts +const steps = ['需求评审', '接口联调', '灰度发布', '线上回归']; +const order = signal(steps); // 顺序唯一真相:组件与下方摘要读同一个信号 +const list = sortableList({ + items: steps, // 文案即排序键,要求唯一 + value: order, + onChange: (next) => toast.success(`已调整:${next[0]} 置顶`), +}); +card({ title: '发布检查单(可拖动,Alt+↑↓ 键盘搬位)' }, + list.el, // 返回的是句柄不是节点:忘了 append(el) 页面上一片空白 + h('p', { class: 'desc' }, () => order().map((t, i) => `${i + 1}. ${t}`).join(' ')), + button({ label: '把「灰度发布」置顶', variant: 'ghost', onClick: () => list.move(2, 0) }), +); +``` + +要点: +- `sortableList` 返回 `{ el, move, order }` 句柄(与 `form` / `virtualList` 同类):节点在 `el` 上,业务侧要改顺序用 `move(from, to)`,读当前顺序用 `order()`。 +- 顺序只存一份:`value` 信号同时喂组件与摘要 / 上报逻辑,否则会出现「界面拖了、提交的仍是老顺序」。 +- `move()` 与拖拽走同一条提交路径(都会触发 `onChange`),所以「置顶 / 上移」按钮不必另写状态同步;越界下标静默忽略,不用自己判边界。 +- 拖到边缘不会自动滚动:条目多到一屏放不下时改用筛选或上下移按钮,不要指望拖出视口;跨列表搬运用两个实例各持一个信号。 +- 条目文案要唯一——重名会让「谁被搬走了」失去参照,需要重复内容时把序号拼进文案(`'确认条款 #1'`)。 + ## 通用约定 - 状态一律放信号(`signal` / `computed`),组件只读写信号,不持有业务数据副本。