diff --git a/app/react/components/Widget/WidgetTabs.tsx b/app/react/components/Widget/WidgetTabs.tsx new file mode 100644 index 000000000..094a41852 --- /dev/null +++ b/app/react/components/Widget/WidgetTabs.tsx @@ -0,0 +1,66 @@ +import { RawParams } from '@uirouter/react'; +import clsx from 'clsx'; +import { ReactNode } from 'react'; + +import { Icon } from '@@/Icon'; +import { Link } from '@@/Link'; + +export interface Tab { + name: string; + icon: ReactNode; + widget: ReactNode; + selectedTabParam: string; +} + +interface Props { + currentTabIndex: number; + tabs: Tab[]; +} + +export function WidgetTabs({ currentTabIndex, tabs }: Props) { + // ensure that the selectedTab param is always valid + const invalidQueryParamValue = tabs.every( + (tab) => encodeURIComponent(tab.selectedTabParam) !== tab.selectedTabParam + ); + + if (invalidQueryParamValue) { + throw new Error('Invalid query param value for tab'); + } + + return ( +
+
+
+ {tabs.map(({ name, icon }, index) => ( + + + {name} + + ))} +
+
+
+ ); +} + +// findSelectedTabIndex returns the index of the selected-tab, or 0 if none is selected +export function findSelectedTabIndex( + { params }: { params: RawParams }, + tabs: Tab[] +) { + const selectedTabParam = params['selected-tab'] || tabs[0].selectedTabParam; + const currentTabIndex = tabs.findIndex( + (tab) => tab.selectedTabParam === selectedTabParam + ); + return currentTabIndex || 0; +} diff --git a/app/react/components/Widget/index.ts b/app/react/components/Widget/index.ts index 92164b168..accc92bdd 100644 --- a/app/react/components/Widget/index.ts +++ b/app/react/components/Widget/index.ts @@ -4,11 +4,13 @@ import { WidgetFooter } from './WidgetFooter'; import { WidgetTitle } from './WidgetTitle'; import { WidgetTaskbar } from './WidgetTaskbar'; import { Loading } from './Loading'; +import { WidgetTabs } from './WidgetTabs'; interface WithSubcomponents { Body: typeof WidgetBody; Footer: typeof WidgetFooter; Title: typeof WidgetTitle; + Tabs: typeof WidgetTabs; Taskbar: typeof WidgetTaskbar; Loading: typeof Loading; } @@ -18,6 +20,7 @@ const Widget = MainComponent as typeof MainComponent & WithSubcomponents; Widget.Body = WidgetBody; Widget.Footer = WidgetFooter; Widget.Title = WidgetTitle; +Widget.Tabs = WidgetTabs; Widget.Taskbar = WidgetTaskbar; Widget.Loading = Loading; @@ -26,6 +29,7 @@ export { WidgetBody, WidgetFooter, WidgetTitle, + WidgetTabs, WidgetTaskbar, Loading, };