Replace reactstrap nav bar with tailwind-based one

This commit is contained in:
Alejandro Celaya
2025-06-16 11:18:03 +02:00
parent 9c0c2fc3f9
commit d10d7fd96d
9 changed files with 81 additions and 139 deletions

View File

@@ -64,7 +64,7 @@ const App: FCWithDeps<AppProps, AppDeps> = (
<div className="tw:h-full">
<MainHeader />
<div className="tw:h-full tw:pt-(--header-height)">
<div className="tw:h-full tw:pt-(--tw-header-height)">
<div
data-testid="shlink-wrapper"
className={clsx(

View File

@@ -1,11 +1,8 @@
import { faChevronDown as arrowIcon, faCogs as cogsIcon } from '@fortawesome/free-solid-svg-icons';
import { faCogs as cogsIcon } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { useToggle } from '@shlinkio/shlink-frontend-kit';
import { clsx } from 'clsx';
import { NavBar } from '@shlinkio/shlink-frontend-kit/tailwind';
import type { FC } from 'react';
import { useEffect } from 'react';
import { Link, useLocation } from 'react-router';
import { Collapse, Nav, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink } from 'reactstrap';
import type { FCWithDeps } from '../container/utils';
import { componentFactory, useDependencies } from '../container/utils';
import { ShlinkLogo } from './img/ShlinkLogo';
@@ -16,39 +13,28 @@ type MainHeaderDeps = {
const MainHeader: FCWithDeps<unknown, MainHeaderDeps> = () => {
const { ServersDropdown } = useDependencies(MainHeader);
const { flag: isNotCollapsed, toggle: toggleCollapse, setToFalse: collapse } = useToggle(false, true);
const location = useLocation();
const { pathname } = location;
// In mobile devices, collapse the navbar when location changes
useEffect(collapse, [location, collapse]);
const { pathname } = useLocation();
const settingsPath = '/settings';
return (
<Navbar color="primary" dark fixed="top" expand="md" className="tw:text-white tw:bg-lm-main tw:dark:bg-dm-main">
<NavbarBrand tag={Link} to="/">
<ShlinkLogo className="tw:inline tw:w-7 tw:mr-1" color="white" /> Shlink
</NavbarBrand>
<NavbarToggler onClick={toggleCollapse}>
<FontAwesomeIcon
icon={arrowIcon}
className={clsx('tw:transition-transform tw:duration-300', { 'tw:rotate-180': isNotCollapsed })}
/>
</NavbarToggler>
<Collapse navbar isOpen={isNotCollapsed}>
<Nav navbar className="tw:ml-auto">
<NavItem>
<NavLink tag={Link} to={settingsPath} active={pathname.startsWith(settingsPath)}>
<FontAwesomeIcon icon={cogsIcon} />&nbsp; Settings
</NavLink>
</NavItem>
<ServersDropdown />
</Nav>
</Collapse>
</Navbar>
<NavBar
className="tw:[&]:fixed tw:top-0 tw:z-900"
brand={(
<Link to="/" className="tw:[&]:text-white tw:no-underline tw:flex tw:items-center tw:gap-2">
<ShlinkLogo className="tw:w-7" color="white" /> <small className="tw:font-normal">Shlink</small>
</Link>
)}
>
<NavBar.MenuItem
to={settingsPath}
active={pathname.startsWith(settingsPath)}
className="tw:flex tw:items-center tw:gap-1.5"
>
<FontAwesomeIcon icon={cogsIcon} /> Settings
</NavBar.MenuItem>
<ServersDropdown />
</NavBar>
);
};

View File

@@ -1,7 +1,6 @@
import { faPlus as plusIcon, faServer as serverIcon } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Link } from 'react-router';
import { DropdownItem, DropdownMenu, DropdownToggle, UncontrolledDropdown } from 'reactstrap';
import { Dropdown, NavBar } from '@shlinkio/shlink-frontend-kit/tailwind';
import type { SelectedServer, ServersMap } from './data';
import { getServerId } from './data';
@@ -14,29 +13,28 @@ export const ServersDropdown = ({ servers, selectedServer }: ServersDropdownProp
const serversList = Object.values(servers);
return (
<UncontrolledDropdown nav inNavbar>
<DropdownToggle nav caret>
<FontAwesomeIcon icon={serverIcon} /> <span className="tw:ml-1">Servers</span>
</DropdownToggle>
<DropdownMenu end className="tw:right-0">
{serversList.length === 0 ? (
<DropdownItem tag={Link} to="/server/create">
<FontAwesomeIcon icon={plusIcon} /> <span className="tw:ml-1">Add a server</span>
</DropdownItem>
) : (
<>
{serversList.map(({ name, id }) => (
<DropdownItem key={id} tag={Link} to={`/server/${id}`} active={getServerId(selectedServer) === id}>
{name}
</DropdownItem>
))}
<DropdownItem divider tag="hr" />
<DropdownItem tag={Link} to="/manage-servers">
<FontAwesomeIcon icon={serverIcon} /> <span className="tw:ml-1">Manage servers</span>
</DropdownItem>
</>
)}
</DropdownMenu>
</UncontrolledDropdown>
<NavBar.Dropdown buttonContent={(
<span className="tw:flex tw:items-center tw:gap-1.5">
<FontAwesomeIcon icon={serverIcon} fixedWidth /> Servers
</span>
)}>
{serversList.length === 0 ? (
<Dropdown.Item to="/server/create">
<FontAwesomeIcon icon={plusIcon} /> Add a server
</Dropdown.Item>
) : (
<>
{serversList.map(({ name, id }) => (
<Dropdown.Item key={id} to={`/server/${id}`} selected={getServerId(selectedServer) === id}>
{name}
</Dropdown.Item>
))}
<Dropdown.Separator />
<Dropdown.Item to="/manage-servers">
<FontAwesomeIcon icon={serverIcon} /> Manage servers
</Dropdown.Item>
</>
)}
</NavBar.Dropdown>
);
};

View File

@@ -8,5 +8,7 @@
:root {
--footer-height: 2.3rem;
--footer-margin: .8rem;
/* Temp alias fo header-height to tw-header-height, so that shlink-web-component uses the right value */
--header-height: var(--tw-header-height);
}
}