Merge branch 'master' into feature/add-changelog-modal/178

This commit is contained in:
Shidorien
2024-01-08 09:43:59 +01:00
63 changed files with 662 additions and 512 deletions
@@ -1,30 +0,0 @@
import { DetailedHTMLProps, Fragment } from "react";
import { BsmIconType } from "../svgs/bsm-icon.component";
type Props<T> = {
className?: string;
tabIndex: number;
tabs: BsContentNavBarTab<T>[];
renderTab: (props: DetailedHTMLProps<React.HTMLAttributes<any>, any>, tab?: BsContentNavBarTab<T>, activeTab?: BsContentNavBarTab<T>) => JSX.Element;
onTabChange?: (index: number, tab?: BsContentNavBarTab<T>) => void;
};
export type BsContentNavBarTab<T = unknown> = {
text: string;
icon?: BsmIconType;
extra?: T;
};
export function BsContentNavBar<T>({ className, tabIndex, tabs, renderTab, onTabChange }: Props<T>) {
const handleTabClick = (index: number) => {
onTabChange?.(index, tabs[index]);
};
return (
<nav className={`h-full grid grid-flow-row ${className ?? ""}`}>
{tabs.map((tab, i) => (
<Fragment key={JSON.stringify(tab)}>{renderTab({ onClick: () => handleTabClick(i) }, tab, tabs[tabIndex])}</Fragment>
))}
</nav>
);
}
@@ -0,0 +1,44 @@
import { LinkBtnProps, LinkButton } from "renderer/components/maps-mangement-components/link-button.component";
import { SvgIcon } from "renderer/components/svgs/svg-icon.type";
import { useTranslation } from "renderer/hooks/use-translation.hook";
export type BsContentTabItemProps<T = unknown> = {
text: string;
icon: SvgIcon;
active?: boolean;
value?: T;
linkProps?: LinkBtnProps;
onClick: (value?: T) => void;
};
type BsContentTabItemComponent<T = unknown> = ({text, icon, active, value, linkProps, onClick}: BsContentTabItemProps<T>) => JSX.Element;
export const BsContentTabItem: BsContentTabItemComponent = ({ text, icon: Icon, active, value, linkProps, onClick }) => {
const t = useTranslation();
const handleClick = (e: React.MouseEvent<HTMLLIElement, MouseEvent>) => {
e.preventDefault();
e.stopPropagation();
onClick(value);
}
return (
<li className={`relative w-full cursor-pointer flex-1 text-center text-lg font-bold flex justify-center items-center content-center px-7 bg-light-main-color-2 dark:bg-main-color-2 hover:bg-light-main-color-1 dark:hover:bg-main-color-1 ${active && "!bg-light-main-color-1 dark:!bg-main-color-1"}`} onClick={handleClick}>
<div className="flex flex-col gap-0.5 justify-start items-center text-main-color-1 dark:text-gray-200">
<Icon className="w-7 h-7"/>
<span className=" font-thin italic text-xs">{t(text)}</span>
</div>
{linkProps && (
<div className="flex items-center absolute top-1.5 left-1.5">
<LinkButton
state={linkProps.state}
className={linkProps.className ?? "block w-6 h-6 aspect-square blur-0 cursor-pointer hover:brightness-75"}
title={linkProps.title}
onClick={linkProps.onClick}
/>
</div>
)}
</li>
);
}
@@ -0,0 +1,35 @@
import { useThemeColor } from "renderer/hooks/use-theme-color.hook";
import { LaserSlider } from "../laser-slider.component";
import { BsContentTabItem, BsContentTabItemProps } from "./bs-content-tab-item.component";
type Props<T = unknown> = Readonly<{
className?: string;
tabs: BsContentTabItemProps<T>[];
tabIndex: number;
onTabChange: (index: number, tab: BsContentTabItemProps<T>) => void;
children: JSX.Element;
}>;
export function BsContentTabPanel({className, tabIndex, tabs, onTabChange, children}: Props) {
const sliderColor = useThemeColor("second-color");
return (
<div className={className ?? "w-full h-full flex flex-row bg-light-main-color-1 dark:bg-main-color-1 rounded-md shadow-black shadow-md overflow-hidden"}>
<nav className="h-full grid grid-flow-row !rounded-none shadow-sm flex-shrink-0">
{tabs.map((tab, i) => (
<BsContentTabItem
key={`${tab.text}${tab.icon}`}
{...tab}
active={tab.active ?? tabIndex === i}
onClick={value => {onTabChange(i, tab); tab.onClick(value);}}
/>
))}
</nav>
<LaserSlider className="h-full w-1 relative shrink-0" mode="vertical" color={sliderColor} nbSteps={tabs.length} step={tabIndex}/>
<div className="flex-grow h-full flex flex-col transition-all duration-300" style={{ translate: `0 ${0 - tabIndex * 100}%` }}>
{children}
</div>
</div>
)
}
@@ -8,7 +8,7 @@ import { LinkOpenerService } from "renderer/services/link-opener.service";
export function BsmIframeView() {
const linkService = useService(LinkOpenerService);
const iframeUrl = useObservable(linkService.iframeLink$);
const iframeUrl = useObservable(() => linkService.iframeLink$);
const ref = useRef(null);
useClickOutside(ref, () => linkService.closeIframe());
@@ -0,0 +1,35 @@
import { CSSProperties } from "react";
type Props = Readonly<{
mode: "horizontal" | "vertical";
color: string;
className?: string;
nbSteps: number;
step: number;
}>
export function LaserSlider({mode, color, className, nbSteps, step}: Props) {
const sliderStyle = ((): CSSProperties => {
if(mode === "vertical"){
return {
transform: `translate(0, ${step * 100}%)`,
height: `calc(100% / ${nbSteps})`,
width: "100%"
}
}
return {
transform: `translate(${step * 100}%, 0)`,
width: `calc(100% / ${nbSteps})`,
height: "100%"
}
})();
return (
<div className={className} style={{ color }}>
<span className="absolute h-full w-full bg-current brightness-50" />
<span className="absolute block bg-current transition-transform duration-300 shadow-center shadow-current" style={sliderStyle} />
</div>
)
}
@@ -1,6 +1,7 @@
import { DetailedHTMLProps, Fragment } from "react";
import { useThemeColor } from "renderer/hooks/use-theme-color.hook";
import { useTranslation } from "renderer/hooks/use-translation.hook";
import { LaserSlider } from "./laser-slider.component";
type Props = {
tabIndex: number;
@@ -22,10 +23,7 @@ export function TabNavBar(props: Props) {
return (
<nav className={`relative h-8 shrink-0 cursor-pointer rounded-md overflow-hidden shadow-md shadow-black bg-light-main-color-2 dark:bg-main-color-2 ${props.className}`}>
<div className="absolute w-full h-1 bottom-0" style={{ color: secondColor }}>
<span className="absolute h-full w-full bg-current brightness-50" />
<span className="absolute h-full block bg-current transition-transform duration-300 shadow-center shadow-current" style={{ transform: `translate(${currentIndex * 100}%, 0)`, width: `calc(100% / ${props.tabsText.length})` }} />
</div>
<LaserSlider className="absolute w-full h-1 bottom-0" mode="horizontal" color={secondColor} nbSteps={props.tabsText.length} step={currentIndex} />
<ul className="grid" style={{ gridTemplateColumns: `repeat(${props.tabsText.length}, minmax(0, 1fr))` }}>
{props.tabsText.map((text, index) =>
props.renderTab ? (