Files
Naiyuan Qing e4537bf57d MUL-5666: cap picker pill width and standardize clear rows (#6358)
* fix(pickers): make the empty value the first row of every picker

Every picker offered "clear this field" in a different place: assignee /
project / stage led with an empty row, priority put "No priority" last,
the date and custom-property popovers hid a Clear button in a footer, and
the project pill carried a hover-only overlay X on top of that.

That X was also broken. It was an absolutely-positioned sibling of the
trigger, and only the picker's own default trigger reserved right padding
for it, so every caller passing `triggerRender` (create dialog, table
cell, autopilot card) had it painted over the project name.

Unify on one rule: the empty value is always the popover's first row.

- Project: drop the overlay X. The "No project" row is now the sole clear
  entry and always renders, so the implicit padding contract that three
  of five callers got wrong is gone.
- Assignee: stop hiding "Unassigned" while searching.
- Priority: split the constant. PRIORITY_ORDER doubles as the sort rank
  (sortIssues uses the array index as the comparison weight), so it keeps
  "none" last; the new PRIORITY_DISPLAY_ORDER leads with it and drives
  the picker, the actions menu, and the filter dropdown.
- Dates and custom properties: the footer Clear button becomes a row
  above the calendar / list, and as a real row it can carry the checkmark
  when the field is empty.
- Stage: give the empty row the value rows' icon column so labels align.

Copy follows the position: rows read as nouns (No project / Unassigned /
No due date / No value), which retires the verb-form clear_action and
clear_date keys across all four locales.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(views): cap the width of pill triggers

Pills carry user-generated text — project title, assignee name, label
names — and the create toolbars are flex-wrap rows, so an uncapped pill
stretches until it owns the whole line and pushes every sibling onto the
next one. Some call sites had already worked around this individually
(the chat composer's project pill, the custom-property pill, the local
directory pill); the shared PillButton had no cap at all.

Cap it once at the shared component: max-w-56, matching the chat
composer. The `min-w-0` + `overflow-hidden` pair is what makes the inner
`truncate` spans work — a flex item only drops `min-width: auto` once its
overflow is hidden, which is why those spans never actually truncated
before. Leading icons are already `shrink-0`, so the text yields first.

Two call-site follow-ups the cap exposed:

- The create-project lead pill had no `truncate`, so a long name would be
  clipped hard instead of ellipsized.
- LabelChip had no `min-w-0`, so the label pill clipped a chip mid-shape
  rather than shrinking it. With it the chip keeps its rounded ends and
  the name inside truncates.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(pickers): keep the empty row out of the search Enter default

Pinning the empty value first also made it the first `data-picker-item`.
PropertyPicker reset the highlight to index 0 on every keystroke and Enter
commits the highlighted row, so typing a query and pressing Enter cleared
the field instead of selecting the match the user was looking at —
"mobile" + Enter detached the project; a member's name + Enter unassigned
the issue.

A second path had the same end: the empty row survives every filter, so a
query with no matches left it as the sole item and the single-result
auto-select fired on it.

Mark the row instead of hiding it. `PickerItem` takes `emptyValue`, which
stamps `data-picker-empty`, and PropertyPicker:

- resolves the post-keystroke highlight to the first row without that
  attribute (-1 when nothing else matched, leaving Enter inert), and
- refuses to auto-select a lone empty row.

The index can't be computed in the input's onChange — the filtered list
hasn't rendered yet — so the keystroke raises a ref flag that an effect
resolves against the fresh DOM. Guarding on the flag is what keeps arrow
keys able to walk back onto the empty row: only typing moves the
highlight off it.

Marked on rows that write null/undefined (project, assignee, stage,
custom property), not on issue priority's "none", which is a real enum
member rather than a cleared field.

Regression tests cover Enter-on-match, Enter-on-no-match, and
arrow-key reachability for project, plus the first two for assignee
(which had no test file). All five fail without this change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 16:16:56 +08:00
..