Repository navigation
Commit 7af2145
fix(plugin-kanban, plugin-gantt): phone kanban opens on its first non-empty column; narrow gantt bars show their title beside the bar (#27435)
<!-- claude-projects-attribution: {"github_login":"os-warren"} -->
_Requested by **Warren** · [project
thread](https://claude.ai/code/project/chan_01U5RdnAAmkSQmZbqh6FtqZ8?thread=cmsg_01U5RdnAAmkSQmZbqh6FtqZ8UjW5hY4zzDTaMsfZutCvCm)_
Fixes #27418
Clause-②: no
Metadata-protocol: none
## 维护者速读
-
改了什么:手机上的看板首次加载时,自动滚到第一个有卡片的列(列顺序不变,用户一动手就不再移动);甘特图上放不下标题的窄条,把完整标题画在条的右侧,宽条照旧在条内带省略号显示。
- 为什么改:HotCRM 18.2.0 手机走查第 12–13 行:管道看板打开停在空的第一阶段;月视图的条只有
18px,标题一个字都显示不出,连省略号都没有。
- 风险与回滚:只动 `plugin-kanban` 的平铺看板滚动器和 `plugin-gantt`
的条标题;桌面宽看板第一个有卡片的列已在视野内时不滚动,宽条(可用宽度 ≥96px)标题位置不变。回滚 = revert 本 PR。
- 先看:`KanbanImpl.tsx` 的 `initialColumnShownRef` 两个
effect;`GanttView.tsx` 的 `barLabelPlacement` 与两处标题渲染。
- 两分钟验证:`pnpm --dir ui exec vitest run
packages/plugin-kanban/src/__tests__/phoneOpensOnFirstFilledColumn-27418.test.tsx
packages/plugin-gantt/src/GanttView.barLabelPlacement-27418.test.tsx`,预期
11 passed。
- 需要你做的:无,席位落地。
## What changes
**Row 12, `ui/packages/plugin-kanban/src/KanbanImpl.tsx` (flat board
only).**
- On its first settled load (`recordsSettled`, and `boardWidth` measured
so columns have their final width), the flat scroller brings the first
column that holds a card into view. It moves by the distance between
that column and the first one, from wherever the snap rests the board
(Chromium rests a phone board at `scrollLeft: 16`).
- It is one-shot. It ends on the first settled load that holds a card,
whether or not it scrolled, and on any `pointerdown` / `touchstart` /
`wheel` / `keydown` on the scroller. A board that settles empty waits
for its first card unless the viewer moved first.
- A column already fully in view is never scrolled to, so a wide desktop
board is unchanged. Column order is unchanged. The swimlane layout is
unchanged.
**Row 13, `ui/packages/plugin-gantt/src/GanttView.tsx` (bar titles).**
- `barLabelPlacement(barWidth, title)` puts the title inside the bar
when the whole title fits after the bar's `px-2`, or when the bar has at
least 96px of room for a truncated title. Otherwise the title is drawn
beside the bar's end (`BESIDE_BAR_LABEL_CLASS`: `left-full`, one line,
`text-foreground`, never truncated). The fit uses a per-character
estimate at the label's 10px font (CJK 10px, other 5.5px, matching
Chromium's measured widths). It only chooses the placement and never
cuts the title.
- Applies to leaf bars and summary bars. Each label now carries
`data-testid="gantt-bar-label-ID"` and `data-placement`.
## Why
Rows 12–13 of #27355 (HotCRM 18.2.0 at phone width), carded as #27418.
Measured in real Chromium (`/opt/pw-browsers/chromium`) at 390x844 on a
fixture page that renders `KanbanBoard` and `GanttView` from source with
the console's Tailwind build. HotCRM's metadata was not used.
| Reading | Before (`45c9be5f`) | After |
|---|---|---|
| Kanban, stages 1–2 empty: column in view on first paint | `scrollLeft`
16, "Prospecting" (empty) | `scrollLeft` 756, "Proposal" (first with
cards) |
| Kanban, stage 1 has cards | `scrollLeft` 16, "Prospecting" |
`scrollLeft` 16, "Prospecting" (unchanged) |
| Gantt month, 18px bar, long title | label box 0px wide inside the bar:
no text, no ellipsis | whole title beside the bar, 297px |
| Gantt week, 31px bar, "Holiday Email Nurture" | 13px inside: an
ellipsis and a letter | whole title beside the bar, 104px |
| Gantt week, 63px bar, long title | 45px inside: "Q4 Ent…" | whole
title beside the bar |
| Gantt month, 112px bar, "Trade Show Booth" | inside, whole | inside,
whole (unchanged) |
| Task-list title cell, long title | `text-overflow: ellipsis`,
`scrollWidth` 356 vs `clientWidth` 91 | unchanged |
## Acceptance notes
- **The task-list title already ellipsizes (premise not reproduced).**
At 390px the title cell measured `text-overflow: ellipsis` with
`scrollWidth` 356 over `clientWidth` 91, and it ellipsized latin, CJK
and unbroken titles in week and month view. A phone opens the gantt with
the task list collapsed (`width: 0`), so the bar was the only place a
title showed; the "cut with no ellipsis" title in the report matches the
bar label (0px of room in an 18px bar). The title cell is not changed. A
test pins the two classes the reading rests on.
- Milestones (diamonds) draw no title before or after; not changed. The
PNG/PDF export's `fitText` labels are not changed either.
- A beside title can sit under a bar's end connector dot while that dot
shows on hover. The label is `pointer-events-none`, so the dot stays
grabbable.
- The kanban's mobile dot indicator still estimates the active column as
`scrollLeft / clientWidth`; it follows the new scroll position like any
other scroll.
- Out of scope per the order: a gantt column-title config key (#27386
row 2).
## Verification
Head `0db321c7`.
- New tests: `phoneOpensOnFirstFilledColumn-27418.test.tsx` (6) and
`GanttView.barLabelPlacement-27418.test.tsx` (5), all pass.
- Ablations (each through `scripts/ablation-replace.mjs`, restored to
the HEAD blob after):
- Delete the kanban `scrollLeft` move: 3 tests fail (opens on third
stage, waits then moves, never moves again).
- Make the kanban `viewerMoved` handler a no-op: 1 test fails (never
overrides a viewer who scrolled first).
- Make `barLabelPlacement` always return `'inside'`: 2 tests fail
(narrow leaf bar, narrow summary bar).
- `pnpm --dir ui exec vitest run packages/plugin-kanban/
packages/plugin-gantt/` under the lock: 186 files passed, 1 skipped;
1320 tests passed, 114 skipped.
- `pnpm --filter @object-ui/plugin-kanban run type-check` and
`@object-ui/plugin-gantt`: exit 0. eslint on the four changed files: 0
errors. `pnpm --dir ui run check:new-line-citations`: 0 new.
<details><summary>Derived gates (dispatch-gates --commands,
35)</summary>
Taken on head `0db321c7`; every command's exit was captured before any
pipe.
```text
node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-empty-changeset.mjs --base origin/main :: exit 0
node scripts/check-empty-changeset.mjs --self-test :: exit 0
node scripts/check-sdui-manifest.mjs :: exit 0
node scripts/check-sdui-manifest.mjs --self-test :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --dir ui exec vitest run scripts/ :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:closed-ceremony-text :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:dispatcher-error-vocabulary :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:engine-double-contract :: exit 0
pnpm check:error-status-conformance :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:pm-changeset-deadline-census :: exit 0
pnpm check:published-files :: exit 0
pnpm check:query-options-erasure :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:skill-identifier-liveness :: exit 0
pnpm check:test-source-alias :: exit 0
pnpm check:tier-file-adoption :: exit 0
pnpm check:type-check-coverage :: exit 0
pnpm check:type-check-debt :: exit 0
pnpm check:undeclared-dep-imports :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:where-matcher :: exit 0
```
`dispatch-gates --ran`: 35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN.
</details>
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` passes (touched files linted;
repo-wide lint is CI's).
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset
(`@object-ui/plugin-kanban` and `@object-ui/plugin-gantt` patch).
- [x] 6. `packages/spec` touched: n/a.
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English (the 维护者速读 block is the
dispatch order's required Chinese summary).
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01JQwxbZnXDgvp4mp2dYYyJg
---
_Generated by [Claude
Code](https://claude.ai/code/session_01JQwxbZnXDgvp4mp2dYYyJg)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent 9229738 commit 7af2145
5 files changed
Lines changed: 387 additions & 7 deletions
File tree
- .changeset
- ui/packages
- plugin-gantt/src
- plugin-kanban/src
- __tests__
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
187 | 187 | | |
188 | 188 | | |
189 | 189 | | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
190 | 234 | | |
191 | 235 | | |
192 | 236 | | |
| |||
4469 | 4513 | | |
4470 | 4514 | | |
4471 | 4515 | | |
4472 | | - | |
4473 | | - | |
4474 | | - | |
| 4516 | + | |
| 4517 | + | |
| 4518 | + | |
| 4519 | + | |
| 4520 | + | |
| 4521 | + | |
| 4522 | + | |
| 4523 | + | |
| 4524 | + | |
| 4525 | + | |
| 4526 | + | |
| 4527 | + | |
| 4528 | + | |
4475 | 4529 | | |
4476 | 4530 | | |
4477 | 4531 | | |
| |||
4843 | 4897 | | |
4844 | 4898 | | |
4845 | 4899 | | |
4846 | | - | |
4847 | | - | |
4848 | | - | |
4849 | | - | |
| 4900 | + | |
| 4901 | + | |
| 4902 | + | |
| 4903 | + | |
| 4904 | + | |
| 4905 | + | |
| 4906 | + | |
| 4907 | + | |
| 4908 | + | |
| 4909 | + | |
| 4910 | + | |
| 4911 | + | |
| 4912 | + | |
| 4913 | + | |
| 4914 | + | |
| 4915 | + | |
4850 | 4916 | | |
4851 | 4917 | | |
4852 | 4918 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1354 | 1354 | | |
1355 | 1355 | | |
1356 | 1356 | | |
| 1357 | + | |
| 1358 | + | |
| 1359 | + | |
| 1360 | + | |
| 1361 | + | |
| 1362 | + | |
| 1363 | + | |
| 1364 | + | |
| 1365 | + | |
| 1366 | + | |
| 1367 | + | |
| 1368 | + | |
| 1369 | + | |
| 1370 | + | |
| 1371 | + | |
| 1372 | + | |
| 1373 | + | |
| 1374 | + | |
| 1375 | + | |
| 1376 | + | |
| 1377 | + | |
| 1378 | + | |
| 1379 | + | |
| 1380 | + | |
| 1381 | + | |
| 1382 | + | |
| 1383 | + | |
| 1384 | + | |
| 1385 | + | |
| 1386 | + | |
| 1387 | + | |
| 1388 | + | |
| 1389 | + | |
| 1390 | + | |
| 1391 | + | |
| 1392 | + | |
| 1393 | + | |
| 1394 | + | |
| 1395 | + | |
| 1396 | + | |
| 1397 | + | |
| 1398 | + | |
| 1399 | + | |
| 1400 | + | |
| 1401 | + | |
1357 | 1402 | | |
1358 | 1403 | | |
1359 | 1404 | | |
| |||
0 commit comments