mirror of
https://github.com/Zagrios/bs-manager.git
synced 2026-07-03 14:08:25 +02:00
[chore] prettify + config prettier
This commit is contained in:
@@ -7,27 +7,24 @@ type Props<T> = {
|
||||
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>) {
|
||||
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>
|
||||
<Fragment key={JSON.stringify(tab)}>{renderTab({ onClick: () => handleTabClick(i) }, tab, tabs[tabIndex])}</Fragment>
|
||||
))}
|
||||
</nav>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,4 +14,4 @@
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { CSSProperties } from "react"
|
||||
import "./bsm-basic-spinner.component.css"
|
||||
import { CSSProperties } from "react";
|
||||
import "./bsm-basic-spinner.component.css";
|
||||
|
||||
type Props = {
|
||||
className?: string,
|
||||
style?: CSSProperties,
|
||||
spinnerClassName?: string,
|
||||
thikness?: string
|
||||
}
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
spinnerClassName?: string;
|
||||
thikness?: string;
|
||||
};
|
||||
|
||||
export function BsmBasicSpinner({className, style, spinnerClassName, thikness = "5px"}: Props) {
|
||||
return (
|
||||
<div className={className} style={style}>
|
||||
<span className={`${spinnerClassName} loader`} style={{border: `${thikness} solid currentColor`, borderBottomColor: "transparent"}}/>
|
||||
</div>
|
||||
)
|
||||
export function BsmBasicSpinner({ className, style, spinnerClassName, thikness = "5px" }: Props) {
|
||||
return (
|
||||
<div className={className} style={style}>
|
||||
<span className={`${spinnerClassName} loader`} style={{ border: `${thikness} solid currentColor`, borderBottomColor: "transparent" }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BsmIcon, BsmIconType } from "../svgs/bsm-icon.component"
|
||||
import { BsmIcon, BsmIconType } from "../svgs/bsm-icon.component";
|
||||
import { useRef, CSSProperties, MouseEvent } from "react";
|
||||
import { BsmImage } from "./bsm-image.component";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
@@ -6,71 +6,91 @@ import { useClickOutside } from "renderer/hooks/use-click-outside.hook";
|
||||
import { useThemeColor } from "renderer/hooks/use-theme-color.hook";
|
||||
import { getCorrectTextColor } from "renderer/helpers/correct-text-color";
|
||||
|
||||
type BsmButtonType = "primary"|"secondary"|"success"|"cancel"|"error";
|
||||
type BsmButtonType = "primary" | "secondary" | "success" | "cancel" | "error";
|
||||
|
||||
type Props = {
|
||||
className?: string,
|
||||
style?: CSSProperties,
|
||||
imgClassName?: string,
|
||||
iconClassName?: string,
|
||||
icon?: BsmIconType,
|
||||
image?: string,
|
||||
text?: string,
|
||||
type?: string,
|
||||
active?: boolean,
|
||||
withBar?: boolean,
|
||||
disabled?: boolean,
|
||||
onClickOutside?: React.ComponentProps<"div">["onClick"],
|
||||
onClick?: React.ComponentProps<"div">["onClick"],
|
||||
typeColor?:BsmButtonType,
|
||||
color?: string,
|
||||
title?: string,
|
||||
iconColor?: string,
|
||||
textClassName?: string
|
||||
}
|
||||
|
||||
export function BsmButton({className, style, imgClassName, iconClassName, icon, image, text, type, active, withBar = true, disabled, onClickOutside, onClick, typeColor, color, title, iconColor, textClassName}: Props) {
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
imgClassName?: string;
|
||||
iconClassName?: string;
|
||||
icon?: BsmIconType;
|
||||
image?: string;
|
||||
text?: string;
|
||||
type?: string;
|
||||
active?: boolean;
|
||||
withBar?: boolean;
|
||||
disabled?: boolean;
|
||||
onClickOutside?: React.ComponentProps<"div">["onClick"];
|
||||
onClick?: React.ComponentProps<"div">["onClick"];
|
||||
typeColor?: BsmButtonType;
|
||||
color?: string;
|
||||
title?: string;
|
||||
iconColor?: string;
|
||||
textClassName?: string;
|
||||
};
|
||||
|
||||
export function BsmButton({ className, style, imgClassName, iconClassName, icon, image, text, type, active, withBar = true, disabled, onClickOutside, onClick, typeColor, color, title, iconColor, textClassName }: Props) {
|
||||
const t = useTranslation();
|
||||
const {firstColor, secondColor} = useThemeColor();
|
||||
const { firstColor, secondColor } = useThemeColor();
|
||||
const ref = useRef(null);
|
||||
|
||||
useClickOutside(ref, onClickOutside);
|
||||
|
||||
const primaryColor = (() => {
|
||||
if(typeColor === "primary"){ return firstColor; }
|
||||
if(typeColor === "secondary"){ return secondColor; }
|
||||
if (typeColor === "primary") {
|
||||
return firstColor;
|
||||
}
|
||||
if (typeColor === "secondary") {
|
||||
return secondColor;
|
||||
}
|
||||
return undefined;
|
||||
})();
|
||||
|
||||
const textColor = (() => {
|
||||
if(primaryColor){
|
||||
return getCorrectTextColor(primaryColor);
|
||||
if (primaryColor) {
|
||||
return getCorrectTextColor(primaryColor);
|
||||
}
|
||||
return typeColor ? "white" : undefined;
|
||||
})();
|
||||
|
||||
const renderTypeColor = (() => {
|
||||
if(!typeColor){ return `bg-light-main-color-2 dark:bg-main-color-2 ${(!withBar && !disabled) && "hover:brightness-125"}`; }
|
||||
if(typeColor === "cancel"){ return "bg-gray-500"; }
|
||||
if(typeColor === "error"){ return "bg-red-500"; }
|
||||
if(typeColor === "success"){ return "bg-green-500"; }
|
||||
if (!typeColor) {
|
||||
return `bg-light-main-color-2 dark:bg-main-color-2 ${!withBar && !disabled && "hover:brightness-125"}`;
|
||||
}
|
||||
if (typeColor === "cancel") {
|
||||
return "bg-gray-500";
|
||||
}
|
||||
if (typeColor === "error") {
|
||||
return "bg-red-500";
|
||||
}
|
||||
if (typeColor === "success") {
|
||||
return "bg-green-500";
|
||||
}
|
||||
return "";
|
||||
})();
|
||||
|
||||
const handleClick = (e: MouseEvent<HTMLDivElement>) => !disabled && onClick?.(e);
|
||||
|
||||
return (
|
||||
<div ref={ref} onClick={handleClick} title={t(title)} className={`${className} overflow-hidden cursor-pointer group ${(!withBar && !disabled && (!!typeColor || !!color)) && "hover:brightness-[1.15]"} ${disabled && "brightness-75 cursor-not-allowed"} ${renderTypeColor}`} style={{...style, backgroundColor: primaryColor || color}}>
|
||||
{ image && <BsmImage image={image} className={imgClassName}/> }
|
||||
{ icon && <BsmIcon icon={icon} className={iconClassName ?? "h-full w-full text-gray-800 dark:text-white"} style={{color: iconColor || textColor}}/> }
|
||||
{text && (type === "submit" ? <button type="submit" className={textClassName || "h-full w-full"} style={{...(!!textColor && {color: textColor})}}>{t(text)}</button> : <span className={textClassName} style={{...(!!textColor && {color: `${textColor}`})}}>{t(text)}</span>)}
|
||||
{ withBar && (
|
||||
<div className="absolute bottom-0 left-0 w-full h-1 bg-current" style={{color: secondColor}}>
|
||||
<div className="absolute top-0 left-0 h-full w-full bg-current brightness-50"/>
|
||||
<div className={`absolute top-0 left-0 h-full w-full bg-inherit -translate-x-full group-hover:translate-x-0 transition-transform shadow-center shadow-current ${active && "translate-x-0"}`}/>
|
||||
<div ref={ref} onClick={handleClick} title={t(title)} className={`${className} overflow-hidden cursor-pointer group ${!withBar && !disabled && (!!typeColor || !!color) && "hover:brightness-[1.15]"} ${disabled && "brightness-75 cursor-not-allowed"} ${renderTypeColor}`} style={{ ...style, backgroundColor: primaryColor || color }}>
|
||||
{image && <BsmImage image={image} className={imgClassName} />}
|
||||
{icon && <BsmIcon icon={icon} className={iconClassName ?? "h-full w-full text-gray-800 dark:text-white"} style={{ color: iconColor || textColor }} />}
|
||||
{text &&
|
||||
(type === "submit" ? (
|
||||
<button type="submit" className={textClassName || "h-full w-full"} style={{ ...(!!textColor && { color: textColor }) }}>
|
||||
{t(text)}
|
||||
</button>
|
||||
) : (
|
||||
<span className={textClassName} style={{ ...(!!textColor && { color: `${textColor}` }) }}>
|
||||
{t(text)}
|
||||
</span>
|
||||
))}
|
||||
{withBar && (
|
||||
<div className="absolute bottom-0 left-0 w-full h-1 bg-current" style={{ color: secondColor }}>
|
||||
<div className="absolute top-0 left-0 h-full w-full bg-current brightness-50" />
|
||||
<div className={`absolute top-0 left-0 h-full w-full bg-inherit -translate-x-full group-hover:translate-x-0 transition-transform shadow-center shadow-current ${active && "translate-x-0"}`} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,30 +1,37 @@
|
||||
import { BsmIcon } from "../svgs/bsm-icon.component"
|
||||
import { BsmIcon } from "../svgs/bsm-icon.component";
|
||||
import { useThemeColor } from "renderer/hooks/use-theme-color.hook";
|
||||
import { getCorrectTextColor } from "renderer/helpers/correct-text-color"
|
||||
import { getCorrectTextColor } from "renderer/helpers/correct-text-color";
|
||||
import { GlowEffect } from "./glow-effect.component";
|
||||
import { motion } from "framer-motion";
|
||||
import { useState } from "react";
|
||||
|
||||
type Props = {className?:string, checked?: boolean, onChange?: (val: boolean) => void, disabled?: boolean}
|
||||
|
||||
export function BsmCheckbox({className, checked, onChange, disabled} : Props) {
|
||||
type Props = { className?: string; checked?: boolean; onChange?: (val: boolean) => void; disabled?: boolean };
|
||||
|
||||
export function BsmCheckbox({ className, checked, onChange, disabled }: Props) {
|
||||
const [hovered, setHovered] = useState(false);
|
||||
const checkedColor = useThemeColor("first-color");
|
||||
const iconColor = checkedColor && getCorrectTextColor(checkedColor);
|
||||
|
||||
const handleClick = () => {
|
||||
if(disabled){ return; }
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
onChange?.(!checked);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<motion.div className={`${className} group-one`} onHoverStart={() => setHovered(true)} onHoverEnd={() => setHovered(false)}>
|
||||
<GlowEffect visible={!disabled && hovered} className="!w-[calc(100%+6px)] !h-[calc(100%+6px)] !-top-[3px] !-left-[3px]"/>
|
||||
<GlowEffect visible={!disabled && hovered} className="!w-[calc(100%+6px)] !h-[calc(100%+6px)] !-top-[3px] !-left-[3px]" />
|
||||
|
||||
<span className={`w-full h-full flex items-center justify-center rounded-md border-2 border-current overflow-hidden bg-light-main-color-3 dark:bg-main-color-1 ${disabled ? "opacity-30 cursor-not-allowed" : "cursor-pointer"}`} onClickCapture={e => {e.stopPropagation(); handleClick()}}>
|
||||
<BsmIcon className="w-full h-full" icon="check" style={{color: iconColor, backgroundColor: checkedColor, visibility: checked ? "visible" : "hidden"}}/>
|
||||
<span
|
||||
className={`w-full h-full flex items-center justify-center rounded-md border-2 border-current overflow-hidden bg-light-main-color-3 dark:bg-main-color-1 ${disabled ? "opacity-30 cursor-not-allowed" : "cursor-pointer"}`}
|
||||
onClickCapture={e => {
|
||||
e.stopPropagation();
|
||||
handleClick();
|
||||
}}
|
||||
>
|
||||
<BsmIcon className="w-full h-full" icon="check" style={{ color: iconColor, backgroundColor: checkedColor, visibility: checked ? "visible" : "hidden" }} />
|
||||
</span>
|
||||
</motion.div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,74 +1,81 @@
|
||||
import { forwardRef, useImperativeHandle, useRef, useState } from "react"
|
||||
import { BsmIconType, BsmIcon } from "../svgs/bsm-icon.component"
|
||||
import { BsmButton } from "./bsm-button.component"
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook"
|
||||
import { AnimatePresence } from "framer-motion"
|
||||
import { useClickOutside } from "renderer/hooks/use-click-outside.hook"
|
||||
import { forwardRef, useImperativeHandle, useRef, useState } from "react";
|
||||
import { BsmIconType, BsmIcon } from "../svgs/bsm-icon.component";
|
||||
import { BsmButton } from "./bsm-button.component";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
import { AnimatePresence } from "framer-motion";
|
||||
import { useClickOutside } from "renderer/hooks/use-click-outside.hook";
|
||||
|
||||
export interface DropDownItem {text: string, icon?: BsmIconType, onClick?: () => void}
|
||||
|
||||
type Props = {
|
||||
className?: string,
|
||||
items?: DropDownItem[],
|
||||
align?: "left"|"right"|"center",
|
||||
withBar?: boolean,
|
||||
icon?: BsmIconType,
|
||||
buttonClassName?: string,
|
||||
menuTranslationY?: string|number
|
||||
children?: JSX.Element,
|
||||
text?: string,
|
||||
export interface DropDownItem {
|
||||
text: string;
|
||||
icon?: BsmIconType;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
export const BsmDropdownButton = forwardRef(({className, items, align, withBar = true, icon = "settings", buttonClassName, menuTranslationY, children, text}: Props, fowardRed) => {
|
||||
type Props = {
|
||||
className?: string;
|
||||
items?: DropDownItem[];
|
||||
align?: "left" | "right" | "center";
|
||||
withBar?: boolean;
|
||||
icon?: BsmIconType;
|
||||
buttonClassName?: string;
|
||||
menuTranslationY?: string | number;
|
||||
children?: JSX.Element;
|
||||
text?: string;
|
||||
};
|
||||
|
||||
const [expanded, setExpanded] = useState(false)
|
||||
const t = useTranslation();
|
||||
const ref = useRef(fowardRed)
|
||||
useClickOutside(ref, () => setExpanded(false));
|
||||
export const BsmDropdownButton = forwardRef(({ className, items, align, withBar = true, icon = "settings", buttonClassName, menuTranslationY, children, text }: Props, fowardRed) => {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const t = useTranslation();
|
||||
const ref = useRef(fowardRed);
|
||||
useClickOutside(ref, () => setExpanded(false));
|
||||
|
||||
useImperativeHandle(fowardRed, () => ({
|
||||
close(){
|
||||
setExpanded(() => false);
|
||||
},
|
||||
open(){
|
||||
setExpanded(() => true);
|
||||
useImperativeHandle(
|
||||
fowardRed,
|
||||
() => ({
|
||||
close() {
|
||||
setExpanded(() => false);
|
||||
},
|
||||
open() {
|
||||
setExpanded(() => true);
|
||||
},
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
const defaultButtonClassName = "relative z-[1] p-1 rounded-md text-inherit w-full h-full shadow-md shadow-black";
|
||||
|
||||
const handleClickOutside = () => {
|
||||
if (children) {
|
||||
return;
|
||||
}
|
||||
}),
|
||||
[],
|
||||
)
|
||||
|
||||
|
||||
const defaultButtonClassName = "relative z-[1] p-1 rounded-md text-inherit w-full h-full shadow-md shadow-black"
|
||||
|
||||
const handleClickOutside = () => {
|
||||
if(children){ return; }
|
||||
setExpanded(false);
|
||||
}
|
||||
};
|
||||
|
||||
const alignClass = (() => {
|
||||
if(align === "center"){ return "right-1/2 origin-top-right translate-x-[50%]" }
|
||||
if(align === "left"){ return "left-0 origin-top-left"; }
|
||||
const alignClass = (() => {
|
||||
if (align === "center") {
|
||||
return "right-1/2 origin-top-right translate-x-[50%]";
|
||||
}
|
||||
if (align === "left") {
|
||||
return "left-0 origin-top-left";
|
||||
}
|
||||
return "right-0 origin-top-right";
|
||||
})()
|
||||
})();
|
||||
|
||||
return (
|
||||
<div ref={ref as unknown as React.LegacyRef<HTMLDivElement>} className={className}>
|
||||
<BsmButton onClick={() => setExpanded(!expanded)} className={buttonClassName ?? defaultButtonClassName} icon={icon} active={expanded} onClickOutside={handleClickOutside} withBar={withBar} text={text}/>
|
||||
<div className={`py-1 w-fit absolute cursor-pointer top-[calc(100%-4px)] rounded-md bg-inherit text-sm text-gray-800 dark:text-gray-200 shadow-md shadow-black transition-[scale] ease-in-out ${alignClass}`} style={{scale: expanded ? "1" : "0", translate: `0 ${menuTranslationY}`}}>
|
||||
{ items?.map((i) => i && (
|
||||
<div key={crypto.randomUUID()} onClick={() => i.onClick?.()} className="flex w-full px-3 py-2 hover:backdrop-brightness-150">
|
||||
{i.icon && <BsmIcon icon={i.icon} className="h-5 w-5 mr-1 text-inherit"/>}
|
||||
<span className="w-max">{t(i.text)}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{!!children && (
|
||||
<AnimatePresence>
|
||||
{expanded && (
|
||||
children
|
||||
return (
|
||||
<div ref={ref as unknown as React.LegacyRef<HTMLDivElement>} className={className}>
|
||||
<BsmButton onClick={() => setExpanded(!expanded)} className={buttonClassName ?? defaultButtonClassName} icon={icon} active={expanded} onClickOutside={handleClickOutside} withBar={withBar} text={text} />
|
||||
<div className={`py-1 w-fit absolute cursor-pointer top-[calc(100%-4px)] rounded-md bg-inherit text-sm text-gray-800 dark:text-gray-200 shadow-md shadow-black transition-[scale] ease-in-out ${alignClass}`} style={{ scale: expanded ? "1" : "0", translate: `0 ${menuTranslationY}` }}>
|
||||
{items?.map(
|
||||
i =>
|
||||
i && (
|
||||
<div key={crypto.randomUUID()} onClick={() => i.onClick?.()} className="flex w-full px-3 py-2 hover:backdrop-brightness-150">
|
||||
{i.icon && <BsmIcon icon={i.icon} className="h-5 w-5 mr-1 text-inherit" />}
|
||||
<span className="w-max">{t(i.text)}</span>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</AnimatePresence>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
</div>
|
||||
{!!children && <AnimatePresence>{expanded && children}</AnimatePresence>}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,41 +1,38 @@
|
||||
import { CSSProperties, forwardRef, SyntheticEvent, useState } from "react";
|
||||
|
||||
type Props = {
|
||||
className?: string,
|
||||
image: string,
|
||||
errorImage?: string,
|
||||
placeholder?: string,
|
||||
loading?: "lazy"|"eager",
|
||||
style?: CSSProperties
|
||||
title?: string,
|
||||
onClick?: (e: MouseEvent) => void
|
||||
}
|
||||
|
||||
export const BsmImage = forwardRef(({className, image, errorImage, placeholder, loading, style, title, onClick}: Props, ref) => {
|
||||
className?: string;
|
||||
image: string;
|
||||
errorImage?: string;
|
||||
placeholder?: string;
|
||||
loading?: "lazy" | "eager";
|
||||
style?: CSSProperties;
|
||||
title?: string;
|
||||
onClick?: (e: MouseEvent) => void;
|
||||
};
|
||||
|
||||
export const BsmImage = forwardRef(({ className, image, errorImage, placeholder, loading, style, title, onClick }: Props, ref) => {
|
||||
const [isLoaded, setIsLoaded] = useState(false);
|
||||
|
||||
image = image || (placeholder||errorImage);
|
||||
image = image || placeholder || errorImage;
|
||||
|
||||
const styles: CSSProperties = (() => {
|
||||
return {
|
||||
...style,
|
||||
...(!isLoaded && {backgroundImage: `url(${placeholder})`}),
|
||||
...(!isLoaded && {backgroundSize: "cover"}),
|
||||
...(!isLoaded && {backgroundPosition: "center"}),
|
||||
}
|
||||
...(!isLoaded && { backgroundImage: `url(${placeholder})` }),
|
||||
...(!isLoaded && { backgroundSize: "cover" }),
|
||||
...(!isLoaded && { backgroundPosition: "center" }),
|
||||
};
|
||||
})();
|
||||
|
||||
const handleError = (e: SyntheticEvent<HTMLImageElement, Event>) => {
|
||||
e.currentTarget.onerror = null;
|
||||
e.currentTarget.src = errorImage || "";
|
||||
}
|
||||
};
|
||||
|
||||
const handleLoaded = () => {
|
||||
setIsLoaded(() => true);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<img ref={ref} title={title} className={className} src={image} loading={loading} onLoad={handleLoaded} onError={handleError} style={styles} onClick={(e) => onClick?.(e)} alt=" " decoding="async"/>
|
||||
)
|
||||
})
|
||||
return <img ref={ref} title={title} className={className} src={image} loading={loading} onLoad={handleLoaded} onError={handleError} style={styles} onClick={e => onClick?.(e)} alt=" " decoding="async" />;
|
||||
});
|
||||
|
||||
@@ -1,24 +1,34 @@
|
||||
import { LinkOpenerService } from "renderer/services/link-opener.service"
|
||||
import { LinkOpenerService } from "renderer/services/link-opener.service";
|
||||
|
||||
type Props = {
|
||||
className?: string,
|
||||
href?: string,
|
||||
internal?: boolean
|
||||
children?: React.ReactNode,
|
||||
style?: React.CSSProperties
|
||||
}
|
||||
|
||||
export function BsmLink({className, href, children, style, internal}: Props) {
|
||||
className?: string;
|
||||
href?: string;
|
||||
internal?: boolean;
|
||||
children?: React.ReactNode;
|
||||
style?: React.CSSProperties;
|
||||
};
|
||||
|
||||
export function BsmLink({ className, href, children, style, internal }: Props) {
|
||||
const linkOpener = LinkOpenerService.getInstance();
|
||||
|
||||
const openLink = () => {
|
||||
if(!href){ return; }
|
||||
if (!href) {
|
||||
return;
|
||||
}
|
||||
linkOpener.open(href, internal);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
// eslint-disable-next-line jsx-a11y/anchor-is-valid -- Will be reworked later
|
||||
<a className={`${className} ${href && "cursor-pointer"}`} onClick={e => {e.stopPropagation(); openLink()}} style={style}>{children}</a>
|
||||
)
|
||||
<a
|
||||
className={`${className} ${href && "cursor-pointer"}`}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
openLink();
|
||||
}}
|
||||
style={style}
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,46 +1,45 @@
|
||||
import { getTrackBackground, Range } from "react-range"
|
||||
import { getCorrectTextColor } from "renderer/helpers/correct-text-color"
|
||||
import { ThemeColor, useThemeColor } from "renderer/hooks/use-theme-color.hook"
|
||||
import { getTrackBackground, Range } from "react-range";
|
||||
import { getCorrectTextColor } from "renderer/helpers/correct-text-color";
|
||||
import { ThemeColor, useThemeColor } from "renderer/hooks/use-theme-color.hook";
|
||||
|
||||
type Props = {
|
||||
colorType?: ThemeColor
|
||||
values: number[],
|
||||
onChange?: (values: number[]) => void,
|
||||
onFinalChange?: (values: number[]) => void,
|
||||
min: number,
|
||||
max: number,
|
||||
step?: number,
|
||||
renderLabel?: (value: number) => JSX.Element,
|
||||
colors?: string[]
|
||||
}
|
||||
|
||||
export function BsmRange({colorType = "first-color", values, onChange, onFinalChange, min, max, renderLabel, step = 1, colors} : Props) {
|
||||
colorType?: ThemeColor;
|
||||
values: number[];
|
||||
onChange?: (values: number[]) => void;
|
||||
onFinalChange?: (values: number[]) => void;
|
||||
min: number;
|
||||
max: number;
|
||||
step?: number;
|
||||
renderLabel?: (value: number) => JSX.Element;
|
||||
colors?: string[];
|
||||
};
|
||||
|
||||
export function BsmRange({ colorType = "first-color", values, onChange, onFinalChange, min, max, renderLabel, step = 1, colors }: Props) {
|
||||
const color = useThemeColor(colorType);
|
||||
const labelTextColor = color && getCorrectTextColor(color);
|
||||
|
||||
return (
|
||||
<Range
|
||||
<Range
|
||||
values={values}
|
||||
min={min}
|
||||
max={max}
|
||||
step={step}
|
||||
onChange={(v) => onChange?.(v)}
|
||||
onFinalChange={(v) => onFinalChange?.(v)}
|
||||
renderTrack={({props, children}) => (
|
||||
onChange={v => onChange?.(v)}
|
||||
onFinalChange={v => onFinalChange?.(v)}
|
||||
renderTrack={({ props, children }) => (
|
||||
<div
|
||||
onMouseDown={props.onMouseDown}
|
||||
onTouchStart={props.onTouchStart}
|
||||
className="w-full rounded-full h-1"
|
||||
{...props}
|
||||
style={{
|
||||
...props.style,
|
||||
background: getTrackBackground({
|
||||
values,
|
||||
colors: colors ?? ["#ccc", color, "#ccc"],
|
||||
min,
|
||||
max
|
||||
}),
|
||||
...props.style,
|
||||
background: getTrackBackground({
|
||||
values,
|
||||
colors: colors ?? ["#ccc", color, "#ccc"],
|
||||
min,
|
||||
max,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
@@ -60,5 +59,5 @@ export function BsmRange({colorType = "first-color", values, onChange, onFinalCh
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,32 +1,33 @@
|
||||
import equal from "fast-deep-equal";
|
||||
import { ComponentProps } from "react"
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook"
|
||||
import { ComponentProps } from "react";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
|
||||
type Props<T> = Omit<ComponentProps<"select">, "onChange"> & {
|
||||
options?: BsmSelectOption<T>[],
|
||||
selected?: T,
|
||||
onChange?: (value: T) => void,
|
||||
}
|
||||
options?: BsmSelectOption<T>[];
|
||||
selected?: T;
|
||||
onChange?: (value: T) => void;
|
||||
};
|
||||
|
||||
export function BsmSelect<T = unknown>(props: Props<T>) {
|
||||
|
||||
const t = useTranslation();
|
||||
|
||||
const handleChange: ComponentProps<"select">["onChange"] = (e) => {
|
||||
const handleChange: ComponentProps<"select">["onChange"] = e => {
|
||||
e.preventDefault();
|
||||
props.onChange?.(props.options?.[parseInt(e.target.value)]?.value);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<select {...props} onChange={handleChange} defaultValue={props.options?.findIndex(opt => equal(opt.value, props.selected))}>
|
||||
{props.options?.map((option, index) => (
|
||||
<option key={JSON.stringify(option)} value={index}>{t(option.text)}</option>
|
||||
<option key={JSON.stringify(option)} value={index}>
|
||||
{t(option.text)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export interface BsmSelectOption<T>{
|
||||
text: string,
|
||||
value: T
|
||||
export interface BsmSelectOption<T> {
|
||||
text: string;
|
||||
value: T;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import { AnimatePresence, motion } from "framer-motion"
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
|
||||
type Props = {
|
||||
visible?: boolean,
|
||||
className?: string
|
||||
}
|
||||
visible?: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function GlowEffect({visible, className}: Props) {
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{visible && <motion.div transition={{duration: .1}} initial={{opacity: 0}} animate={{opacity: 1}} exit={{opacity: 0}} className={`${className} glow-on-hover`}/>}
|
||||
</AnimatePresence>
|
||||
)
|
||||
export function GlowEffect({ visible, className }: Props) {
|
||||
return <AnimatePresence>{visible && <motion.div transition={{ duration: 0.1 }} initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} className={`${className} glow-on-hover`} />}</AnimatePresence>;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { useRef } from "react";
|
||||
import { useClickOutside } from "renderer/hooks/use-click-outside.hook";
|
||||
import { useObservable } from "renderer/hooks/use-observable.hook"
|
||||
import { useObservable } from "renderer/hooks/use-observable.hook";
|
||||
import { LinkOpenerService } from "renderer/services/link-opener.service";
|
||||
|
||||
export function BsmIframeView() {
|
||||
|
||||
const linkService = LinkOpenerService.getInstance()
|
||||
const linkService = LinkOpenerService.getInstance();
|
||||
|
||||
const iframeUrl = useObservable(linkService.iframeLink$);
|
||||
const ref = useRef(null);
|
||||
@@ -16,13 +15,12 @@ export function BsmIframeView() {
|
||||
<AnimatePresence>
|
||||
{iframeUrl && (
|
||||
<div className="top-0 absolute w-screen h-screen flex content-center items-center justify-center z-[150]">
|
||||
<motion.span className="absolute w-full h-full bg-black top-0 right-0" initial={{opacity: 0}} animate={{opacity: .60}} exit={{opacity: 0}} transition={{duration: .2}}/>
|
||||
<motion.div ref={ref} className="z-[1] w-[calc(100vw-250px)] h-[calc(100vh-250px)] shadow-black shadow-md rounded-md overflow-hidden bg-black" initial={{y: "100vh"}} animate={{y: 0}} exit={{y: "100vh"}}>
|
||||
<iframe className="w-full h-full" src={iframeUrl}/>
|
||||
<motion.span className="absolute w-full h-full bg-black top-0 right-0" initial={{ opacity: 0 }} animate={{ opacity: 0.6 }} exit={{ opacity: 0 }} transition={{ duration: 0.2 }} />
|
||||
<motion.div ref={ref} className="z-[1] w-[calc(100vw-250px)] h-[calc(100vh-250px)] shadow-black shadow-md rounded-md overflow-hidden bg-black" initial={{ y: "100vh" }} animate={{ y: 0 }} exit={{ y: "100vh" }}>
|
||||
<iframe className="w-full h-full" src={iframeUrl} />
|
||||
</motion.div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</AnimatePresence>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,15 +3,14 @@ import { useThemeColor } from "renderer/hooks/use-theme-color.hook";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
|
||||
type Props = {
|
||||
tabIndex: number
|
||||
tabsText: string[],
|
||||
onTabChange: (index: number) => void,
|
||||
className?: string,
|
||||
renderTab?: (props: DetailedHTMLProps<React.HTMLAttributes<any>, any>, text: string, index?: number) => JSX.Element
|
||||
}
|
||||
tabIndex: number;
|
||||
tabsText: string[];
|
||||
onTabChange: (index: number) => void;
|
||||
className?: string;
|
||||
renderTab?: (props: DetailedHTMLProps<React.HTMLAttributes<any>, any>, text: string, index?: number) => JSX.Element;
|
||||
};
|
||||
|
||||
export function TabNavBar(props: Props) {
|
||||
|
||||
const currentIndex = props.tabIndex ?? 0;
|
||||
|
||||
const t = useTranslation();
|
||||
@@ -19,26 +18,25 @@ export function TabNavBar(props: Props) {
|
||||
|
||||
const selectTab = (index: number) => {
|
||||
props.onTabChange(index);
|
||||
}
|
||||
};
|
||||
|
||||
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}}>
|
||||
<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})`}}/>
|
||||
<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>
|
||||
<ul className="grid" style={{gridTemplateColumns: `repeat(${props.tabsText.length}, minmax(0, 1fr))`}}>
|
||||
{props.tabsText.map((text, index) => (
|
||||
props.renderTab ? (
|
||||
<Fragment key={text}>
|
||||
{props.renderTab({onClick: () => selectTab(index)}, t(text), index)}
|
||||
</Fragment>
|
||||
) : (
|
||||
<li className="px-4 h-full text-center text-gray-800 dark:text-gray-200 text-lg font-bold hover:backdrop-brightness-75" key={text} onClick={() => selectTab(index)}>{t(text)}</li>
|
||||
)
|
||||
|
||||
))}
|
||||
<ul className="grid" style={{ gridTemplateColumns: `repeat(${props.tabsText.length}, minmax(0, 1fr))` }}>
|
||||
{props.tabsText.map((text, index) =>
|
||||
props.renderTab ? (
|
||||
<Fragment key={text}>{props.renderTab({ onClick: () => selectTab(index) }, t(text), index)}</Fragment>
|
||||
) : (
|
||||
<li className="px-4 h-full text-center text-gray-800 dark:text-gray-200 text-lg font-bold hover:backdrop-brightness-75" key={text} onClick={() => selectTab(index)}>
|
||||
{t(text)}
|
||||
</li>
|
||||
)
|
||||
)}
|
||||
</ul>
|
||||
</nav>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user