---
title: 'Data View Controller Followups: Animation, Header Merge, Tag Display, Tag Editing'
type: task
status: draft
description: >-
  Post-merge polish for the data-view controller redesign: restore the trigger-expand animation into
  a centered modal (or bottom sheet on mobile), merge the pinned current-view header into the
  in-list selected ViewItem, render per-view tag chips on ViewItem, and wire TagInput for
  adding/removing user tags with autocomplete suggestions sourced from existing + visible auto/llm
  tags.
base_uri: user:task/league/data-view-controller-followup-animation-tags-header.md
created_at: '2026-05-12T04:50:13.262Z'
entity_id: 881f7817-eedf-47e5-ba0a-d12c0a51fc6a
observations:
  - ████████████████████████████████████████████████
owner_identity_uri: █████████████████████████
priority: Medium
public_read: true
relations:
  - succeeds [[█████████████████████████-████-████-██████████-████-████████-█████████-███████]]
tags:
  - user:tag/league-xo-football.md
updated_at: '2026-06-08T15:47:56.081Z'
visibility_analyzed_at: '2026-11-05T23:48:42.038Z'
---

# Data View Controller Followups: Animation, Header Merge, Tag Display, Tag Editing

Post-merge polish for the ██████████████████████████████████████████████████████████
work. Browser testing surfaced four issues. All four are scoped to the react-table
`table-view-controller/` directory plus the league prop bag for suggestions; no
backend changes are needed (organization API already deployed).

## Background

The parent task (`██████████████████████████████████████████████████████████`,
status Completed) was merged at react-table SHA `81829d6`. The pre-redesign
behavior is pinned at SHA `bee61b55`. Reference architecture:
[[user:text/league/data-view-storage-architecture.md]].

Key files:

- `repository/active/react-table/src/table-view-controller/table-view-controller.js`
- `repository/active/react-table/src/table-view-controller/table-view-controller.styl`
- `repository/active/react-table/src/table-view-controller/current-view-header.js`
- `repository/active/react-table/src/table-view-controller/view-item.js`
- `repository/active/react-table/src/table-view-controller/tag-input.js`
- `repository/active/react-table/src/table-view-controller/tag-chip.js`
- `repository/active/league/app/views/pages/data-views/data-views.js` (prop bag)
- `repository/active/league/app/core/data-views/selectors.js`
  (`get_data_view_organization_props_for_table_view_controller`)

Workflow: [[user:workflow/software/implement-general-task.md]]. Open one worktree per repo
via [[user:workflow/software/setup-worktree.md]] and merge via
[[user:workflow/software/merge-worktree.md]]. Bump react-table SHA in `league/package.json`
line 188 and clear `.yarn/cache/react-table-*.zip` before `yarn install`.

## Scope

### Task 1 -- Restore trigger-expand animation into centered modal

The pre-redesign `.table-expanding-control-container` had width/height/transform
transitions at 250ms cubic-bezier(0.4, 0, 0.2, 1), and `.-open` set
`width 60vw; height 80vh`. The trigger button visibly grew into the panel in
place; JS computed a `translateX` to horizontally center the expanded result.

The redesign replaced this with `position: fixed; top: 50%; left: 50%;
transform: translate(-50%, -50%) !important;` on `.-open`, which teleports the
panel and overrides the JS transform -- no animation is possible.

Fix: revert to in-place expansion. Animate width/height (and box-shadow) on the
container; keep the JS-computed `translateX` for horizontal centering. For
`<768px` add the bottom-sheet variant via a slide-up `translateY` transition
plus width/height growth. Use the same 250ms cubic-bezier(0.4, 0, 0.2, 1) curve
the original used, and reuse `MENU_CLOSE_TIMEOUT` for the closing flag so the
exit transition plays before unmount.

Verification: open and close the controller several times on desktop and a
mobile viewport (DevTools <768px). The trigger button visibly grows into the
modal and shrinks back; no teleport or flash.

- [ ] 1a. Remove `position: fixed`, the centering rule, and `!important` from
      `.table-view-controller.-open` in `table-view-controller.styl`. Restore the
      width/height transition pattern from `bee61b55`.
- [ ] 1b. Re-add the 250ms transition on `.table-expanding-control-container`
      for `transform`, `width`, `height`, `box-shadow` (already present in
      `src/styles/table-expanding-control-container.styl:32`, confirm it still
      applies once the `.-open` overrides are removed).
- [ ] 1c. Add a `<768px` bottom-sheet variant (width 100vw, height 90vh,
      anchored to bottom) that animates from the trigger position. Use the existing
      JS `set_transform` hook to compute a `translateY` shift if needed.
- [ ] 1d. Verify exit animation plays via the existing `closing` state and
      `MENU_CLOSE_TIMEOUT` timing.
- [ ] 1e. Manual browser verification at >=768px and <768px.

### Task 2 -- Merge `CurrentViewHeader` into the in-list current-view `ViewItem`

The pinned `CurrentViewHeader` and the highlighted in-list current-view row
duplicate name/description/username. Goal: a single representation in the list,
with the existing auto-scroll-to-selected keeping it findable.

Approach: move the header's action buttons (favorite, save, reset, edit,
duplicate) onto `ViewItem` when `view.view_id === selected_view.view_id`, then
remove the `CurrentViewHeader` mount from `table-view-controller.js`. Delete
`current-view-header.js` entirely; nothing else imports it (verify with a grep).

The currently-selected `ViewItem` will need extra props (the action callbacks
and the favorite + table-state-changed booleans). Plumb them through from the
controller. Keep the existing `.misc-menu` (MoreHorizIcon Popper) for the
non-selected items; the selected item shows its inline actions in addition to,
or instead of, the misc menu.

- [ ] 2a. Extend `ViewItem` props: `is_selected_current_view`, `is_favorited`,
      `is_table_state_changed`, `on_toggle_favorite`, `on_save_current_view`,
      `on_reset_current_view`. When `is_selected_current_view` is true, render the
      inline action row using the same icon set/tooltips as `CurrentViewHeader`.
- [ ] 2b. Update `table-view-controller.js`: pass the new props to each
      `ViewItem`; remove the `<CurrentViewHeader>` mount and its container styles.
- [ ] 2c. Delete `current-view-header.js`. Strip the related
      `.tvc-current-view-header*` rules from `table-view-controller.styl`.
- [ ] 2d. Confirm auto-scroll still lands on `.table-view-item.-selected` (no
      change needed -- the selector already targets it).
- [ ] 2e. Manual browser verification: actions still work; the selected view is
      visually anchored at scroll center on open.

### Task 3 -- Render per-view tag chips on `ViewItem`

`use_organized_views` already attaches a combined `tags` array (user + llm +
auto) to each view (`use-organized-views.js:39-46`). `ViewItem` currently
ignores it. Render the chips using `TagChip` with the same source-styling rules
as the rail.

- [ ] 3a. In `view-item.js`, render a chip row beneath the
      `table-view-item-info-container` using `view.tags || []`. Truncate to a
      reasonable max (e.g. 6) with a `+N` overflow indicator if the list is long.
- [ ] 3b. Add a `.table-view-item-tags` style block in
      `table-view-controller.styl`: flex-wrap row, gap 4px, top margin 4px. Reuse
      existing `.tvc-tag-chip` styles unchanged.
- [ ] 3c. Manual browser verification: tag chips appear on every view, color-
      coded by source.

### Task 4 -- Mount `TagInput` for per-view tag add/remove

`TagInput` exists but is not mounted. Wire it into `ViewItem` (probably gated
behind a small "edit tags" affordance or revealed on hover for the selected/
editable views) using the already-available `on_add_user_tag` and
`on_remove_user_tag` props. The remove control already exists on `TagChip` via
`on_remove`; pass it through when the view is editable.

Suggestions for the autocomplete must include:

1. The user's existing tag names across all their views (from
   `tags_by_view_id`).
2. Auto and llm tag names visible in the current view set (computed in
   `data-views.js`).

Compute this union in `data-views.js` (B14) and pass it as a new
`tag_suggestions` prop on `TableViewController`. The controller forwards it to
each `ViewItem` and into `TagInput`.

- [ ] 4a. In `data-views.js`, compute `tag_suggestions` as
      `Array<string>` -- union of all `tags_by_view_id` names plus all
      `derive_auto_tags`-derived names across `data_views`. Memoize.
- [ ] 4b. Add `tag_suggestions: PropTypes.arrayOf(PropTypes.string)` to
      `TableViewController` propTypes. Default to `[]`. Pass to each `ViewItem`.
- [ ] 4c. In `ViewItem`, render `TagInput` (with `suggestions={tag_suggestions}`
      and `on_submit={(name) => on_add_user_tag(view.view_id, name)}`) inline below
      the chip row when `view.is_editable` is truthy. Pass an `on_remove` callback
      to each user-source `TagChip` that calls
      `on_remove_user_tag(view.view_id, tag.name)`.
- [ ] 4d. Plumb `on_add_user_tag` and `on_remove_user_tag` from the controller
      into every `ViewItem` (currently they live only on the controller).
- [ ] 4e. Manual browser verification: typing a partial name surfaces
      suggestions; pressing Enter creates the tag and clears the input; clicking
      the X on a user-source chip removes it; auto/llm chips have no X.

### Task 5 -- Unblock stale quick-filter-sort task

The redesign was its blocker and is merged. Remove the `[blocked-by]`
observation and the `blocked_by` relation on
[[user:task/league/data-views-quick-filter-sort-implementation-plan.md]];
leave status unchanged.

- [ ] 5a. `base entity update` to drop the `blocked_by` relation and the
      `[blocked-by]` observation.

## Worktree and merge

- react-table worktree branch: `data-view-controller-followups`
- league worktree branch: `data-view-controller-followups`
- After react-table merges to main, capture the new SHA and bump
  `league/package.json` line 188, clear `.yarn/cache/react-table-*.zip`, then
  `yarn install` and verify the bundle. Merge league via
  [[user:workflow/software/merge-worktree.md]].

## Out of scope

- Backend changes (organization API already deployed).
- Plays-views (still empty per parent-task rebase).
- Tag-palette management UI (deferred indefinitely per parent rebase).
- Anything beyond the four issues listed; if more drift surfaces during
  implementation, stop and revise this plan.
