Conversation
|
This PR has conflicts. You need to rebase the PR before it can be merged. |
|
This PR doesn't have conflicts anymore. It can be merged after all status checks have passed and it has been reviewed. |
r1chm8
force-pushed
the
laz-947
branch
2 times, most recently
from
August 25, 2026 11:35
42b895d to
276239c
Compare
|
This PR has conflicts. You need to rebase the PR before it can be merged. |
…947) The mods page now renders Page/PageHeader/PageScroll/PageContent and the new Toolbar instead of the legacy MainPage chrome and IconBar, and its table gets the edge-to-edge sticky-header treatment the extensions page uses. The toolbar takes actions rather than components, so a new hook adapts the mod-icons registrations for it — hidden when a condition returns false, disabled when it returns a string, ordered by the registered position, exactly as IconBar showed them. Anything that doesn't fit the width collapses into the overflow menu, which ToolbarGroup already handles. The five toolbar entries that were React components had no equivalent in the action shape, so they are now plain actions with their behaviour carried over: Install From File, Check for Updates, Deploy Mods and Purge Mods in the hook, Manage Rules as a plain registration in mod-dependency-manager. The button components they replace are deleted; CheckModVersionsButton stays because it is still the table's row action. Deliberate differences, all of which the toolbar redesign revisits: - Deploy is highlighted via brand colour rather than the flashing class - Manage Rules loses its unresolved-conflict emphasis - a string condition greys an action out without showing the explanation E2E selectors move from the ids those components carried to the data-testid the toolbar renders.
A sticky-header table scrolls with the page rather than in a pane of its own, so it carries `height: auto` and stopped at its last row. The details pane is positioned against that container, which meant double-clicking a row in a table holding one row opened a pane one row tall. The container now also takes `flex-auto`, and the mods and extensions pages lay their scroll content out as full-height flex columns so there is height to grow into. `flex-auto` rather than `flex-1` keeps the basis on the content, so a table taller than the page still scrolls it. Both page-header toolbars take `flex-1 justify-end` for the same sort of reason: as a flex item a toolbar is sized by its content, and a toolbar sized by its content can't tell how much room it has, so it kept every action and ran over the page title instead of collapsing into its overflow menu. Growing rather than only shrinking is what lets it expand again when space returns. The mods toolbar moves out of ModList into a component of its own.
The row reads Install From File, Open, History, Check for Updates, Categories, Manage Rules, which moves Open to position 30 and History to 40. Position is a single ordering for the row and the overflow menu both, so those two move up the menu as well. Open is new here: the five folder actions move out of `mod-icons` into a group of their own, which the page folds into one button with a menu behind it, so they take one slot on the row between them. `ToolbarPanelButton` now honours `panelRole`, so that menu is styled as a dropdown and focuses its own first row rather than being treated as a panel of settings — it hardcoded the latter. Icons: Manage Rules asks for a "rules" icon of its own rather than borrowing the profile transfer "connection" one, Install From File takes a plus, and an icon name we have no path for now shows a puzzle piece — such a name comes from an extension — rather than a generic shape.
…ton each (LAZ-947) IconBar grouped every `mod-icons` action sharing an icon into one dropdown, labelled with whatever leading words their titles had in common. That is how the five "Open ..." actions came to sit behind an "Open" button, and the two "Import From ..." ones behind an "Import" button, without anyone asking for it. The new toolbar only knew about a hardcoded `mod-icons-open` group, so any action that had relied on the icon grouping became a button of its own — Witcher 3's "Open TW3 Documents Folder", and both importers. An action now lands in one of those menus either by registering into the menu's group, which is how to ask for it going forward, or by registering into `mod-icons` under the menu's icon, which is what extensions we don't ship still do. A menu holding a single action renders as that action rather than a menu of one, the way IconBar did, so a game supporting only one importer doesn't grow a button to reach it.
…LAZ-947) A menu row now takes the same `brand` a Button does, so an action reads the same on a toolbar as it does in the overflow menu it collapsed into — Deploy Mods kept its primary colour on the bar and lost it the moment it collapsed. `IToolbarAction` inherits the brand from `IMenuAction` rather than declaring its own, so there is one union behind both. A row tints its icon and leaves the labels an even column of text to read down; `danger` is the exception and colours the whole row, a destructive action being the one thing in a menu that should be hard to pick by accident. `danger` was already a dropdown-item style but no Button brand answered to it, so it becomes a real brand: the token ramp was there, only the `nxm-button-danger` block and the union value were missing. `DropdownItem` takes a brand too, which lets ToolRow's Delete row drop the class it applied by hand. Also renames `nxm-dropdown-item-active` to `-focus`, along with the props and state behind it. Headless UI 2 deprecated the `active` render prop in favour of `focus`, and in a stylesheet `-active` reads against CSS's own `:active`, which means something else entirely.
…pty state (LAZ-947) Four things that all belonged to the bottom half of the page. The table's multi-selection bar moves out of the table and into the page's own bottom section, above the drop panel. `Table` takes a `footerContainer` and portals the footer into it, so the bar lays out in the page's flow instead of absolutely filling the bottom of the table — which also frees the 48px the rows used to give up to a placeholder keeping them clear of it. A table that passes no container keeps the footer where it was, which is every other table. The drop panel gains a wrapper of its own to position against, since the bar is now a sibling of it inside that section: the collapse toggle is absolute, and with the `relative` still on the section it would have been positioning against a box holding both. Collapsing reaches only the dropzone. The hint standing in for the bar below two selections becomes an `Alert`. It is still dismissed for good — `UsageAlert` keeps the decision in `settings.interface.usage` under the same id `Usage` used, so anyone who has already dismissed it won't see this one either. `Usage` stays for its four other callers, which use the reopenable `persistent` mode this doesn't offer. "Get more mods" stops sticking to the bottom of the page as you scroll and sits after the last row, the page rather than the table being what scrolls now. Its translucent band goes with the sticky, nothing passing behind it any more. The empty state becomes `NoResults`, and its way out is a button named for the game — "Get Skyrim SE mods" rather than "But don't worry, I know a place...", which moves up to the message. Where more than one source offers somewhere to browse it becomes a menu, on the design-system `Dropdown` rather than the bootstrap one the bar still uses. The filter deciding that is now shared with the bar rather than written out twice. The details pane a row opens on a double click is positioned against the table container, and a sticky-header table is as tall as its rows and as wide as its columns — so the pane stood the full height of the table and slid out of view as the page scrolled sideways. A layer between the two sticks to the top right of the page scroll instead and stands as tall as it, which keeps the pane on the part of the table you can see. The container spans the columns the table overflows into, sticking only carrying as far as the containing block, and the scroll region becomes a size container so the layer has something to measure. The pane's collapse button takes the scrollbar's width as padding, or its icon would sit behind a horizontal scrollbar.
The Open and Import buttons get the ellipsis IconBar's dropdown had, so a button that leads to a choice of actions reads as one. It is appended rather than part of the label, so the words stay the translated ones and the menu keeps them as its own accessible name rather than announcing "Open dot dot dot". Manage Rules says how many conflicts are waiting in its tooltip, where the button used to flash for them. An action can now carry a `notice` in its options — what it has to say beyond its title, which the toolbar puts in brackets after it and which therefore reaches the tooltip, that being the label. It is read through the store so it follows state: a conflict check dispatches after the change in mods that set it going, so a notice read once at registration, or even at the render that change caused, would be stale. mod-dependency-manager owns the text, as it owns the state behind it. It now records how many conflicts have neither a rule nor a resolution rather than only that some have neither, so the tooltip can count them: "3 unresolved conflicts", or "Unresolved conflicts" without a count. The boolean stays, derived from the count, because uploadCollection reads it.
Giving the mods table's footer a place of its own meant `renderFooter` had to choose between the portal and the placeholder, and it wrapped whatever the footer turned out to be. But only the multi-row action bar needs the placeholder: that bar is absolutely positioned so it fills a surrounding panel, and the placeholder is what holds the rows clear of it. The usage hint shown below two selections lays out in the flow and takes its own height, so a placeholder boxes it at 48px — on every table that passes no `footerContainer`, which is all of them but the mods one.
The classic UI is still a choice the user has — Settings, Theme, "Use legacy Vortex UI" — and this branch had left it with the redesigned page, because a page's `newLayout` was decided without reference to which UI was running. Design asked for the old toolbar back. An IconBar in the redesigned header read as neither one thing nor the other, so the classic UI gets the whole page it had instead. `newLayout` now also takes a callback, as `visible` already does, so a page that has both renderings decides for itself which one it is about to draw. The mods page reads the classic/modern setting; every other page keeps passing a plain `true` and is untouched. `PagesContext` resolves it inside the memo that already watches that setting, so the answer follows a change to it rather than being fixed when the page registered — the toggle needs no restart — and `MainPageContainer` is handed a resolved boolean rather than having to subscribe for itself. ModList draws whichever of the two the layout asks for. The classic one is what it always was: the toolbar in the chrome's header, the table in a panel with its footer inside it, the drop zone below a FlexLayout. It needs the legacy `.main-page` wrappers, its header being a portal into one of them — `MainPageHeader` renders nothing without that portal, which is how the toolbar came to go missing in the first place. `ActivationButton`, `DeactivationButton` and `InstallArchiveButton` come back for it, being what that bar drew for Deploy, Purge and Install From File, along with `staticButtons` for the two the page contributes itself rather than registering. `renderMoreModsLink` comes back for the empty state, reusing the `browsableSources` filter the redesigned page already had rather than repeating it. Each bar draws only what it can. A component can't become an action — the row needs a label and an icon it can measure, promote, collapse into the overflow and let the user pin — so Deploy and Purge are registered as classic-only components and the new toolbar skips them, building its own pair instead. Manage Rules is registered once for each: the button that flashes while conflicts wait, and the action that says how many in its tooltip. `isClassicOnly` and `isModernOnly` already existed for this and were already honoured on the global-icons bars; they now mean something on a page toolbar too, which is what lets one set of registrations feed two bars. open-directory goes back to registering into `mod-icons` rather than a group of its own. Its five "Open ..." actions still land behind the new toolbar's Open button, which adopts them by their shared `open-ext` icon exactly as it does Witcher 3's — and the group it had moved to has no classic consumer, so the old bar would have lost all five. What the new toolbar cannot draw is now said at debug rather than warned: it fires on every render, and a component registration reaching the classic bar is the arrangement now rather than a surprise. The tutorials dropdown is the one that does — still gone from the new toolbar, as the parent story asks, still on the old one.
|
This PR doesn't have conflicts anymore. It can be merged after all status checks have passed and it has been reviewed. |
Contributor
Author
|
This PR has conflicts. You need to rebase the PR before it can be merged. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

https://linear.app/nexus-mods/issue/LAZ-947/implement-redesign-of-mods-page-layout-toolbar-and-table
Page & toolbar — The page renders
Page/PageHeader/PageScroll/PageContent and the new Toolbar instead of MainPage chrome and IconBar; the table gets the edge-to-edge sticky-header treatment the extensions page uses. A new hook adapts the existing mod-icons registrations into toolbar actions — hidden on a false condition, disabled on a string, ordered by registered position, as IconBar showed them. The five entries that were React components become plain actions (Install From File, Check for Updates, Deploy, Purge in the hook; Manage Rules in mod-dependency-manager) and their button components are deleted.
Order & icons
The row reads Install From File, Open, History, Check for Updates, Categories, Manage Rules. The "Open …" and "Import From …" actions each fold behind one button again, now via explicit groups rather than IconBar's icon-matching; a menu holding one action renders as that action. Manage Rules gets a rules icon of its own, Install From File a plus, and an unknown icon name shows a puzzle piece.
Menu/Button primitives
Menu rows take the same brand a Button does, so an action looks the same on the bar and in the overflow menu it collapses into; danger becomes a real Button brand; nxm-dropdown-item-active is renamed -focus (Headless UI 2 deprecated active, and -active misreads against CSS :active).
Bottom of the page
The multi-selection bar portals out of the table into the page's own bottom section via a new footerContainer prop (other tables keep the footer where it was), freeing the 48px the rows gave up to a placeholder. The two-selection hint becomes UsageAlert, keyed the same as Usage so existing dismissals carry. "Get more mods" stops sticking to the bottom, and the empty state becomes NoResults with a game-named button (or menu, where several sources offer one).
Table sizing & details pane
A sticky-header table's container takes flex-auto and spans the columns it overflows into. The details pane a double click opens now sticks to the top/bottom/right of the page scroll rather than standing the full height of the table and sliding out of view on horizontal scroll; PageScroll becomes a size container so it has something to measure.
Toolbar labels
The Open and Import buttons get their
...back, appended to the label so the words stay the translated key. Manage Rules now names its unresolved conflicts in the tooltip ("Manage Rules (3 unresolved conflicts)") instead of losing the flash it used to get: an action can carry anoticein its options, which the toolbar brackets after the title.mod-dependency-managerowns the text and now stores the count, not just a boolean.Deliberate differences,
All revisited by the toolbar redesign: Deploy is highlighted by brand colour rather than the flashing class.