mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 09:30:31 +03:00
Extracted nav pills to their own component for reusability
This commit is contained in:
parent
cf5205e976
commit
b0fa14fcfe
6 changed files with 54 additions and 45 deletions
|
@ -47,7 +47,7 @@ const App = (
|
|||
<div className={classNames('shlink-wrapper', { 'd-flex d-md-block align-items-center': isHome })}>
|
||||
<Routes>
|
||||
<Route index element={<Home />} />
|
||||
<Route path="/settings" element={<Settings />} />
|
||||
<Route path="/settings/*" element={<Settings />} />
|
||||
<Route path="/manage-servers" element={<ManageServers />} />
|
||||
<Route path="/server/create" element={<CreateServer />} />
|
||||
<Route path="/server/:serverId/edit" element={<EditServer />} />
|
||||
|
|
|
@ -31,7 +31,7 @@ const MainHeader = (ServersDropdown: FC) => () => {
|
|||
<Collapse navbar isOpen={isOpen}>
|
||||
<Nav navbar className="ml-auto">
|
||||
<NavItem>
|
||||
<NavLink tag={Link} to={settingsPath} active={pathname === settingsPath}>
|
||||
<NavLink tag={Link} to={settingsPath} active={pathname.startsWith(settingsPath)}>
|
||||
<FontAwesomeIcon icon={cogsIcon} /> Settings
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
|
|
|
@ -1,18 +1,11 @@
|
|||
import { FC, ReactNode } from 'react';
|
||||
import { Row } from 'reactstrap';
|
||||
import { Navigate, Routes, Route } from 'react-router-dom';
|
||||
import { NoMenuLayout } from '../common/NoMenuLayout';
|
||||
import { NavPills } from '../utils/NavPills';
|
||||
|
||||
const SettingsSections: FC<{ items: ReactNode[][] }> = ({ items }) => (
|
||||
const SettingsSections: FC<{ items: ReactNode[] }> = ({ items }) => (
|
||||
<>
|
||||
{items.map((child, index) => (
|
||||
<Row key={index}>
|
||||
{child.map((subChild, subIndex) => (
|
||||
<div key={subIndex} className={`col-lg-${12 / child.length} mb-3`}>
|
||||
{subChild}
|
||||
</div>
|
||||
))}
|
||||
</Row>
|
||||
))}
|
||||
{items.map((child, index) => <div key={index} className="mb-3">{child}</div>)}
|
||||
</>
|
||||
);
|
||||
|
||||
|
@ -25,13 +18,20 @@ const Settings = (
|
|||
Tags: FC,
|
||||
) => () => (
|
||||
<NoMenuLayout>
|
||||
<SettingsSections
|
||||
<NavPills
|
||||
items={[
|
||||
[ <UserInterface />, <Visits /> ], // eslint-disable-line react/jsx-key
|
||||
[ <ShortUrlCreation />, <ShortUrlsList /> ], // eslint-disable-line react/jsx-key
|
||||
[ <Tags />, <RealTimeUpdates /> ], // eslint-disable-line react/jsx-key
|
||||
{ to: 'app', children: 'App' },
|
||||
{ to: 'short-urls', children: 'Short URLs' },
|
||||
{ to: 'others', children: 'Others' },
|
||||
]}
|
||||
/>
|
||||
|
||||
<Routes>
|
||||
<Route path="app" element={<SettingsSections items={[ <UserInterface key="one" />, <RealTimeUpdates key="two" /> ]} />} />
|
||||
<Route path="short-urls" element={<SettingsSections items={[ <ShortUrlCreation key="one" />, <ShortUrlsList key="two" /> ]} />} />
|
||||
<Route path="others" element={<SettingsSections items={[ <Tags key="one" />, <Visits key="two" /> ]} />} />
|
||||
<Route path="*" element={<Navigate replace to="app" />} />
|
||||
</Routes>
|
||||
</NoMenuLayout>
|
||||
);
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
@import '../utils/base';
|
||||
@import './base';
|
||||
|
||||
.visits-stats__nav {
|
||||
.nav-pills__nav {
|
||||
position: sticky !important;
|
||||
top: $headerHeight - 1px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.visits-stats__nav-link {
|
||||
.nav-pills__nav-link {
|
||||
border-radius: 0 !important;
|
||||
padding-bottom: calc(.5rem - 3px) !important;
|
||||
border-bottom: 3px solid transparent;
|
||||
|
@ -19,11 +19,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
.visits-stats__nav-link:hover {
|
||||
.nav-pills__nav-link:hover {
|
||||
color: $mainColor !important;
|
||||
}
|
||||
|
||||
.visits-stats__nav-link.active {
|
||||
.nav-pills__nav-link.active {
|
||||
border-color: $mainColor;
|
||||
background-color: var(--primary-color) !important;
|
||||
color: $mainColor !important;
|
17
src/utils/NavPills.tsx
Normal file
17
src/utils/NavPills.tsx
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { FC, ReactNode } from 'react';
|
||||
import { Card, Nav, NavLink } from 'reactstrap';
|
||||
import { NavLink as RouterNavLink } from 'react-router-dom';
|
||||
import './NavPills.scss';
|
||||
|
||||
interface NavPillsProps {
|
||||
items: { children: ReactNode; to: string; replace?: boolean }[];
|
||||
}
|
||||
|
||||
export const NavPills: FC<NavPillsProps> = ({ items }) => (
|
||||
<Card className="nav-pills__nav p-0 overflow-hidden mb-3" body>
|
||||
<Nav pills fill>
|
||||
{items.map(({ children, ...rest }, index) =>
|
||||
<NavLink key={index} className="nav-pills__nav-link" tag={RouterNavLink} {...rest}>{children}</NavLink>)}
|
||||
</Nav>
|
||||
</Card>
|
||||
);
|
|
@ -1,11 +1,10 @@
|
|||
import { isEmpty, propEq, values } from 'ramda';
|
||||
import { useState, useEffect, useMemo, FC, useRef } from 'react';
|
||||
import { Button, Card, Nav, NavLink, Progress, Row } from 'reactstrap';
|
||||
import { Button, Progress, Row } from 'reactstrap';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faCalendarAlt, faMapMarkedAlt, faList, faChartPie, faFileDownload } from '@fortawesome/free-solid-svg-icons';
|
||||
import { IconDefinition } from '@fortawesome/fontawesome-common-types';
|
||||
import { Route, Routes, NavLink as RouterNavLink, Navigate } from 'react-router-dom';
|
||||
import { Location } from 'history';
|
||||
import { Route, Routes, Navigate } from 'react-router-dom';
|
||||
import classNames from 'classnames';
|
||||
import { DateRangeSelector } from '../utils/dates/DateRangeSelector';
|
||||
import Message from '../utils/Message';
|
||||
|
@ -16,6 +15,7 @@ import { Settings } from '../settings/reducers/settings';
|
|||
import { SelectedServer } from '../servers/data';
|
||||
import { supportsBotVisits } from '../utils/helpers/features';
|
||||
import { prettify } from '../utils/helpers/numbers';
|
||||
import { NavPills } from '../utils/NavPills';
|
||||
import LineChartCard from './charts/LineChartCard';
|
||||
import VisitsTable from './VisitsTable';
|
||||
import { NormalizedOrphanVisit, NormalizedVisit, VisitsFilter, VisitsInfo, VisitsParams } from './types';
|
||||
|
@ -25,7 +25,6 @@ import { VisitsFilterDropdown } from './helpers/VisitsFilterDropdown';
|
|||
import { HighlightableProps, highlightedVisitsToStats } from './types/helpers';
|
||||
import { DoughnutChartCard } from './charts/DoughnutChartCard';
|
||||
import { SortableBarChartCard } from './charts/SortableBarChartCard';
|
||||
import './VisitsStats.scss';
|
||||
|
||||
export interface VisitsStatsProps {
|
||||
getVisits: (params: VisitsParams, doIntervalFallback?: boolean) => void;
|
||||
|
@ -55,19 +54,6 @@ const sections: Record<Section, VisitsNavLinkProps> = {
|
|||
|
||||
let selectedBar: string | undefined;
|
||||
|
||||
const VisitsNavLink: FC<VisitsNavLinkProps & { to: string }> = ({ subPath, title, icon, to }) => (
|
||||
<NavLink
|
||||
tag={RouterNavLink}
|
||||
className="visits-stats__nav-link"
|
||||
to={to}
|
||||
isActive={(_: null, { pathname }: Location) => pathname.endsWith(`visits${subPath}`)}
|
||||
replace
|
||||
>
|
||||
<FontAwesomeIcon icon={icon} />
|
||||
<span className="ml-2 d-none d-sm-inline">{title}</span>
|
||||
</NavLink>
|
||||
);
|
||||
|
||||
const VisitsStats: FC<VisitsStatsProps> = ({
|
||||
children,
|
||||
visitsInfo,
|
||||
|
@ -157,12 +143,18 @@ const VisitsStats: FC<VisitsStatsProps> = ({
|
|||
|
||||
return (
|
||||
<>
|
||||
<Card className="visits-stats__nav p-0 overflow-hidden" body>
|
||||
<Nav pills fill>
|
||||
{Object.entries(sections).map(([ section, props ]) =>
|
||||
<VisitsNavLink key={section} {...props} to={buildSectionUrl(props.subPath)} />)}
|
||||
</Nav>
|
||||
</Card>
|
||||
<NavPills
|
||||
items={Object.values(sections).map(({ title, icon, subPath }) => ({
|
||||
replace: true,
|
||||
to: buildSectionUrl(subPath),
|
||||
children: (
|
||||
<>
|
||||
<FontAwesomeIcon icon={icon} />
|
||||
<span className="ml-2 d-none d-sm-inline">{title}</span>
|
||||
</>
|
||||
),
|
||||
}))}
|
||||
/>
|
||||
<Row>
|
||||
<Routes>
|
||||
<Route
|
||||
|
|
Loading…
Reference in a new issue