Repository navigation
Commit 08ed7e2
refactor(app-shell): move ObjectView's view tab strip, plugin schema and header chrome into sibling hooks (slice 9) (#26412)
<!-- claude-projects-attribution: {"github_login":"os-steve"} -->
_Requested by **Steve** · [project
thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi9VtKf3F9dzJPJM4RRtABaEc)_
Part of #25992 (slice 9 of the move-only split; slices 1 to 8 landed as
#26152, #26233, #26310, #26336, #26353, #26374 and #26396, and the card
stays open for the slices listed below).
Clause-②: no
Metadata-protocol: none
## What moved
Base `e4434f75` (slice 8 merged, latest `main` merged in).
`ui/packages/app-shell/src/views/ObjectView.tsx` is 2554 lines there.
Three concerns of `ObjectViewInner` move verbatim into custom hooks in
three new sibling modules. In each, every input is passed as an
argument, typed from its source (no `any`), the component destructures
the returned values under their original names, no identifier is renamed
and no context is added.
### N: the view tab strip → `objectViewTabBar.tsx`
Lines 1131–1206 move into `useObjectViewTabBar({ views, savedViews,
objectDef, servedViews, viewLabel, unlabelledViewText, language, t,
viewAccess, activeViewId, handleViewChange, isAdmin, handleAddView,
handleRenameView, handleDeleteView, handlePinView, handleSetDefaultView,
handleConfigView, setManageViewsOpen })`. That covers the View Tabs
docblock, `useViewTabsFit()`, `useDisplayLocale()`, `viewTabItems`, the
`<ViewTabBar>` node (`viewTabBar`, objectui#12440) and its own-row face
`viewTabRow` (objectui#13114). It returns `viewTabItems`, `viewTabBar`
and `viewTabRow`. The view handlers it wires are typed from the
`ViewTabBarProps` callbacks they are handed to.
### P (plugin schema): the merged views and `ObjectViewSchema` →
`objectViewPluginSchema.ts`
Lines 1975–2034 move into `useObjectViewPluginSchema({ views,
activeViewId, viewDraft, activeView, objectDef, onEdit, isAdmin,
location, navigate, viewId, servedViews, viewLabel, unlabelledViewText,
language })`: `mergedViews`, the active view's `userActions` toolbar
policy (objectui#11013) and `objectViewSchema`. It returns `mergedViews`
and `objectViewSchema`. `viewDraft` is declared in the component as
`useState<Record<string, any> | null>`, so it is typed as
`Partial<ReturnType<typeof buildViewTabs>[number]> | null`: a partial of
the tab it is merged over, which is the only way the block reads it.
### P (header chrome): the create button, folded header actions and
refused-view face → `objectViewHeaderChrome.tsx`
Lines 2036–2146 move into `useObjectViewHeaderChrome({ objectCanCreate,
createVisible, createDisabled, actions, t, listActions,
toolbarActionsWithDeepLink, identityImportEnabled, objectCanImport,
importVisible, importDisabled, setShowImport, listFavorite,
activeViewHasPreference, handleResetView, isEnvironmentList, toolbarBar,
viewAccess })`: `showCreate`, `listFabClearance`, `createButton`,
`toolbarRowActions`, `primaryToolbarAction`, `importShown`,
`foldedMenuBar`, `foldedHeaderActions` (objectui#12705) and
`refusedViewFace` (objectui#12112). It returns `showCreate`,
`listFabClearance`, `createButton`, `importShown`, `foldedHeaderActions`
and `refusedViewFace`. The block calls no React hook; it keeps the `use`
name so it reads like its siblings and stays at the top level of the
component.
Each hook is called where its block stood, so the order of React hooks
in the component is unchanged.
### Imports
Each import moves with its only readers. `ViewTabBar`, `ViewTabItem`,
`useViewTabsFit`, `useDisplayLocale` and `pickLocalized` go to the tab
bar module. `Empty`, `EmptyTitle`, `EmptyDescription`, `Lock`,
`ActionBarSchema`, `UIActionSchema` and `MOBILE_FAB_LIST_CLEARANCE` go
to the header chrome module. `declaredPrimaryToolbarAction` also goes
there; it is still re-exported from `ObjectView.tsx`, line unchanged.
Imports still read elsewhere in the component (`viewTabLabel`,
`viewRowId`, `VIEW_TYPE_ICONS`, `normalizeListViewSchema`, `Button`,
`Plus`, `SchemaRenderer`) are imported by the modules as well. Nothing
new is exported from `ObjectView.tsx`, and the package entry is
untouched. `ObjectView.tsx` goes from 2554 to 2374 lines.
Move-only proof: the multiset of non-blank lines of the base
`ObjectView.tsx` is contained in new `ObjectView.tsx` plus the three
modules. The only base lines missing are 11 import lines or import-list
entries that lost a moved name: the `@object-ui/plugin-view` value and
type imports, `Empty` / `EmptyTitle` / `EmptyDescription` in the
`@object-ui/components` list, the `lucide-react`, `@object-ui/types`,
`@object-ui/i18n` and `./objectViewChrome.js` imports, and the
`declaredPrimaryToolbarAction` and `MOBILE_FAB_LIST_CLEARANCE` entries.
The trimmed lines replace them. The added lines are the trimmed imports
and the three new imports, the module headers and imports, the hook
signatures and parameter types, the `return` objects, and in
`ObjectView.tsx` the three calls with their destructuring and two-line
comments.
## Test edits
None. No source reader of `ObjectView.tsx` names a moved line: the
readers that mention `ViewTabBar`, `userActions` or
`normalizeListViewSchema` read `plugin-view/src/ObjectView.tsx`, mock
the module, or anchor on `fullSchema` inside `renderListView`, which
stays.
## Seam table rows this PR changes (measured on `e4434f75`)
The full table (regions A to Q) is in #26336.
| # | Concern | Lines on `e4434f75` | Reads | Read by | Slice |
|---|---|---|---|---|---|
| N | View tab items, `ViewTabBar`, its own-row face | 1131–1206 | A, C,
F (`savedViews`, `views`, `activeViewId`, `viewAccess`), I (view
handlers) | O, Q | **9, this PR** |
| P (schema) | Merged views, `userActions` toolbar policy,
`ObjectViewSchema` | 1975–2034 | A, B (`viewDraft`), C, F | Q | **9,
this PR** |
| P (header chrome) | Create button, folded header actions, refused-view
face | 2036–2146 | A, C (`listFavorite`), D, E, F (`viewAccess`), G, H,
J (`actions`) | Q | **9, this PR** |
## Remaining slices (the last closes the card)
- **Slice 10 — Q:** the JSX return's `PageHeader`, import wizard and
`NavigationOverlay` blocks into sub-components with explicit props.
- **Slice 11 — B + I + O + G-columns (closes the card):** the
view-config and view-tab CRUD handlers, `renderListView` and the column
flags only it reads. The `writeRefusalCensus-11583` rows, the
`relayRungCensus-7559` read and the `check-i18n-call-site-keys.test.ts`
`deleteViewConfirm` row are re-pointed (path and file-key changes only).
## Verification
See the os-dev-report comment on the card for the full readings:
app-shell `type-check` after rebuilding its dependency closure, every
`ObjectView.*` test in `views/` (the view tab, header fold, mobile FAB,
refused view and favorites tests among them), the `AppContent*` tests,
every other test that reads `ObjectView.tsx` as source, eslint on the 4
touched files, the derived dispatch gates, and a Console build.
## Acceptance notes
- eslint: 0 errors, no new warning. The four files report 113 warnings
against 116 for the base `ObjectView.tsx` alone: three
`react-hooks/preserve-manual-memoization` warnings are no longer raised,
and the two `exhaustive-deps` warnings on `handleViewChange` only moved
lines.
Changeset: `.changeset/25992-objectview-tab-bar-header-chrome-split.md`
(patch, `@object-ui/app-shell`).
---
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 411fc00 commit 08ed7e2
5 files changed
Lines changed: 518 additions & 257 deletions
File tree
- .changeset
- ui/packages/app-shell/src/views
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
0 commit comments