add checkbox

This commit is contained in:
MathieuG-P
2022-09-24 22:33:46 +02:00
parent fbb2eca661
commit 90de63c35f
13 changed files with 105 additions and 59 deletions
@@ -1,22 +0,0 @@
.glow-on-hover {
border-radius: 10px;
background: linear-gradient(45deg, #ff0000, #ff7300, #fffb00, #48ff00, #00ffd5, #002bff, #7a00ff, #ff00c8, #ff0000);
position: absolute;
top: -4px;
left:-4px;
background-size: 400%;
z-index: -1;
filter: blur(3px);
width: calc(100% + 8px);
height: calc(100% + 8px);
animation: glowing 20s linear infinite;
opacity: 0;
transition: opacity .2s ease-in-out;
border-radius: 10px;
}
@keyframes glowing {
0% { background-position: 0 0; }
50% { background-position: 400% 0; }
100% { background-position: 0 0; }
}
@@ -40,7 +40,7 @@ export const AvailableVersionItem = memo(function AvailableVersionItem(props: {v
return (
<div className="group m-3 relative w-72 h-60 transition-transform active:scale-[.98]" onClick={toggleSelect}>
<div className={`absolute glow-on-hover group-hover:opacity-100 ${selected && "opacity-100"}`}></div>
<div className={`absolute glow-on-hover group-hover:opacity-100 ${selected && "opacity-100"}`}/>
<div className={`relative flex flex-col overflow-hidden rounded-md w-72 h-60 cursor-pointer group-hover:shadow-none duration-300 bg-light-main-color-2 dark:bg-main-color-2 ${!selected && "shadow-lg shadow-gray-900"}`}>
<BsmImage image={props.version.ReleaseImg ? props.version.ReleaseImg : defaultImage} errorImage={defaultImage} placeholder={defaultImage} className="absolute top-0 right-0 w-full h-full opacity-40 blur-xl object-cover" loading="lazy"/>
<BsmImage image={props.version.ReleaseImg ? props.version.ReleaseImg : defaultImage} errorImage={defaultImage} placeholder={defaultImage} className="bg-black w-full h-3/4 object-cover" loading="lazy"/>
@@ -4,6 +4,7 @@ import React from "react";
import { BsmImage } from "./bsm-image.component";
import { useTranslation } from "renderer/hooks/use-translation.hook";
import { useThemeColor } from "renderer/hooks/use-theme-color.hook";
import { useBestTextColor } from "renderer/hooks/use-best-text-color.hook";
type BsmButtonType = "primary"|"success"|"cancel"|"error";
@@ -32,16 +33,7 @@ export function BsmButton({className, style, imgClassName, iconClassName, icon,
const primaryColor = typeColor === "primary" ? useThemeColor("first-color") : null;
const textColor = !primaryColor ? null : (() => {
const hex = primaryColor.replaceAll("#", "");
const uicolors = [parseInt(hex.substring(0, 2), 16) / 255, parseInt(hex.substring(0, 2), 16) / 255, parseInt(hex.substring(0, 2), 16) / 255];
const c = uicolors.map(col => {
if (col <= 0.03928) { return col / 12.92; }
return Math.pow((col + 0.055) / 1.055, 2.4);
});
const L = (0.2126 * c[0]) + (0.7152 * c[1]) + (0.0722 * c[2]);
return (L > 0.179) ? "#000" : "fff";
})();
const textColor = !primaryColor ? null : useBestTextColor(primaryColor);
const renderTypeColor = (() => {
if(typeColor === "primary"){ return ""; }
@@ -57,7 +49,7 @@ export function BsmButton({className, style, imgClassName, iconClassName, icon,
<div onClick={e => onClick && onClick(e)} className={`${className} overflow-hidden cursor-pointer group ${(!withBar && (!!typeColor || !!color)) && "hover:brightness-[1.15]"} ${disabled && "brightness-75 cursor-not-allowed"} ${renderTypeColor}`} style={{...style, ...((!!primaryColor || !!color) && {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"}/> }
{text && (type === "submit" ? <button className="w-full h-full" style={{...(!!textColor && {color: textColor})}}>{t(text)}</button> : <span style={{...(!!textColor && {color: textColor})}} >{t(text)}</span>)}
{text && (type === "submit" ? <button type="submit" className="w-full h-full" style={{...(!!textColor && {color: textColor})}}>{t(text)}</button> : <span 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"/>
@@ -0,0 +1,32 @@
import { useState } from "react"
import { BsmIcon } from "../svgs/bsm-icon.component"
import { motion } from "framer-motion"
import { useThemeColor } from "renderer/hooks/use-theme-color.hook";
import { useBestTextColor } from "renderer/hooks/use-best-text-color.hook";
type Props = {className?:string, checked?: boolean, onChange?: (val: boolean) => void, disabled?: boolean}
export function BsmCheckbox({className, checked, onChange, disabled} : Props) {
const [isChecked, setIsChecked] = useState(true);
const checkedColor = useThemeColor("first-color");
const iconColor = useBestTextColor(checkedColor);
const handleClick = () => {
if(disabled){ return; }
onChange && onChange(!checked);
setIsChecked(val => !val);
}
return (
<div className={`group ${className}`}>
{!disabled && <div className="glow-on-hover !w-[calc(100%+6px)] !h-[calc(100%+6px)] !-top-[3px] !-left-[3px] group-hover:opacity-100"/>}
<div className={`w-full h-full bg-inherit border-2 rounded-md overflow-hidden flex items-center justify-center relative ${disabled ? "brightness-75 cursor-not-allowed" : "cursor-pointer"}`} onClick={handleClick}>
<motion.span className="absolute w-full h-full flex items-center justify-center" style={{backgroundColor: checkedColor}} initial={isChecked ? {scale: 1} : {scale: 0}} animate={isChecked ? {scale: 1} : {scale: 0}} transition={{duration: .1}}>
<BsmIcon icon="check" style={{color: iconColor}}/>
</motion.span>
</div>
</div>
)
}
@@ -20,9 +20,10 @@ import EditIcon from "./icons/edit-icon.component";
import { ExportIcon } from "./icons/export-icon.component";
import PatreonIcon from "./icons/patreon-icon.component";
import { SearchIcon } from "./icons/search-icon.component";
import { CheckIcon } from "./icons/check-icon.component";
export type BsmIconType = (
"settings"|"trash"|"favorite"|"folder"|"bsNote"|
"settings"|"trash"|"favorite"|"folder"|"bsNote"|"check"|
"terminal"|"desktop"|"oculus"|"add"|"cross"|"task"|
"copy"|"steam"|"edit"|"export"|"patreon"|"search"|
"fr-FR-flag"|"es-ES-flag"|"en-US-flag"|"en-EN-flag"
@@ -52,6 +53,7 @@ export const BsmIcon = memo(({className, icon, style}: {className?: string, icon
if(icon === "export"){ return <ExportIcon className={className} style={style}/> }
if(icon === "patreon"){ return <PatreonIcon className={className} style={style}/> }
if(icon === "search"){ return <SearchIcon className={className} style={style}/> }
if(icon === "check"){ return <CheckIcon className={className} style={style}/> }
return <TrashIcon className={className} style={style}/>
}
@@ -0,0 +1,9 @@
import { CSSProperties } from "react";
export function CheckIcon(props: {className?: string, style?: CSSProperties}) {
return (
<svg className={props.className} style={props.style} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40" height="40" width="40">
<path fill="currentColor" d="M15.792 29.458q-.292 0-.563-.104-.271-.104-.521-.354l-7.416-7.417q-.459-.458-.459-1.145 0-.688.459-1.146.458-.459 1.104-.459.646 0 1.146.459l6.25 6.291 14.666-14.666q.459-.459 1.125-.459.667 0 1.125.459.459.458.459 1.145 0 .688-.459 1.146L16.917 29q-.25.25-.521.354-.271.104-.604.104Z"/>
</svg>
)
}
@@ -31,7 +31,7 @@ export default function TitleBar({template = "main"} : {template?: "update"|"mai
}
return (
<header id="titlebar" className="min-h-[22px] bg-light-main-color-1 dark:bg-main-color-1 w-screen h-[22px] flex content-center items-center justify-start z-10">
<header id="titlebar" className="min-h-[22px] bg-light-main-color-1 shrink-0 dark:bg-main-color-1 w-screen h-[22px] flex content-center items-center justify-start z-10">
<div id="drag-region" className='grow basis-0 h-full'>
{template !== "update" && (<div id="window-title" className='pl-1'>
<span className='text-gray-800 dark:text-gray-100 font-bold text-xs italic'>BSManager</span>
@@ -1,3 +1,4 @@
import { BsmCheckbox } from "renderer/components/shared/bsm-checkbox.component"
import { Mod } from "shared/models/mods/mod.interface"
type Props = {mod: Mod, installed: Mod}
@@ -5,8 +6,8 @@ type Props = {mod: Mod, installed: Mod}
export function ModItem({mod, installed}: Props) {
return (
<>
<div className="h-full aspect-square flex items-center justify-center p-2 rounded-l-md bg-main-color-1 ml-3">
<span className="w-full h-full border-white border-2 rounded-md"></span>
<div className="h-full aspect-square flex items-center justify-center p-[7px] rounded-l-md bg-main-color-1 ml-3">
<BsmCheckbox className="h-full aspect-square z-[1] relative bg-main-color-1" disabled/>
</div>
<span className="bg-main-color-1 py-2 pl-3 font-bold text-sm whitespace-nowrap">{mod.name}</span>
<span className="min-w-0 text-center bg-main-color-1 py-2 px-1 text-sm">{installed?.version || "-"}</span>
@@ -11,8 +11,6 @@ export function ModsGrid({modsMap, installed}: Props) {
const [filter, setFilter] = useState("");
const [filterEnabled, setFilterEnabled] = useState(false);
const inputRef = useRef();
const installedMod = (key: string, mod: Mod): Mod => {
if(!installed || !installed.get(key)){ return; }
return installed.get(key).find(m => m.name === mod.name);
@@ -27,29 +25,29 @@ export function ModsGrid({modsMap, installed}: Props) {
return modsMap && (
<>
<div className="grid gap-y-1 grid-cols-[40px_min-content_min-content_min-content_1fr]">
<span className="sticky flex items-center justify-center top-0 bg-main-color-2 border-b-2 border-main-color-1">
<span className="z-10 sticky flex items-center justify-center top-0 bg-main-color-2 border-b-2 border-main-color-1">
<div className="pl-4">
<BsmButton className="rounded-full h-6 p-[2px]" withBar={false} icon="search" onClick={handleToogleFilter}/>
</div>
</span>
<div className="sticky top-0 flex items-center bg-main-color-2 border-main-color-1 border-b-2 h-8 w-52 px-1">
<div className="z-10 sticky top-0 flex items-center bg-main-color-2 border-main-color-1 border-b-2 h-8 px-1">
{(filterEnabled ? (
<motion.input autoFocus ref={inputRef} className="bg-main-color-1 rounded-md h-6 px-2" initial={{width: 0}} animate={{width: "100%"}} onChange={e => handleInput(e.target.value)}/>
<motion.input autoFocus className="bg-main-color-1 rounded-md h-6 px-2" initial={{width: 0}} animate={{width: "250px"}} transition={{ease:"easeInOut", duration:.15}} onChange={e => handleInput(e.target.value)}/>
):(
<span className="w-full text-center">Nom</span>
))}
</div>
<span className="sticky flex items-center justify-center text-sm top-0 bg-main-color-2 border-b-2 border-main-color-1 h-8 px-2">Installé</span>
<span className="sticky flex items-center justify-center text-sm top-0 bg-main-color-2 border-b-2 border-main-color-1 h-8 px-2">Dernière</span>
<span className="sticky flex items-center justify-center text-sm top-0 bg-main-color-2 border-b-2 border-main-color-1 h-8">Description</span>
<span className="z-10 sticky flex items-center justify-center text-base top-0 bg-main-color-2 border-b-2 border-main-color-1 h-8 px-2">Installé</span>
<span className="z-10 sticky flex items-center justify-center text-base top-0 bg-main-color-2 border-b-2 border-main-color-1 h-8 px-2">Dernière</span>
<span className="z-10 sticky flex items-center justify-center text-base top-0 bg-main-color-2 border-b-2 border-main-color-1 h-8">Description</span>
{
Array.from(modsMap.keys()).map(key => (
<>
<span className="col-span-full py-1 font-bold pl-3" key={key}>{key}</span>
Array.from(modsMap.keys()).map(key => modsMap.get(key).some(mod => mod.name.toLowerCase().includes(filter)) && (
<div key={key} className="contents">
<span className="col-span-full py-1 font-bold pl-3">{key}</span>
{modsMap.get(key).map(mod => mod.name.toLowerCase().includes(filter) && <ModItem key={mod.name} mod={mod} installed={installedMod(key, mod)}/>)}
</>
</div>
))
}
</div>
@@ -22,14 +22,12 @@ export function ModsSlide({version}: {version: BSVersion}) {
}
useEffect(() => {
if(!isVisible){ return; }
if(!modsAvailable){
console.log("OUI");
modsManager.getAvailableMods(version).then(mods => setModsAvailable(modsToCategoryMap(mods)));
modsManager.getInstalledMods(version).then(mods => setModsInstalled(modsToCategoryMap(mods)));
}
}, [isVisible]);
if(!isVisible){ return; }
modsManager.getAvailableMods(version).then(mods => setModsAvailable(modsToCategoryMap(mods)));
modsManager.getInstalledMods(version).then(mods => setModsInstalled(modsToCategoryMap(mods)));
}, [isVisible, version]);
return (
@@ -0,0 +1,14 @@
export function useBestTextColor(bgHex: string): string{
return (() => {
const hex = bgHex.replaceAll("#", "");
const uicolors = [parseInt(hex.substring(0, 2), 16) / 255, parseInt(hex.substring(0, 2), 16) / 255, parseInt(hex.substring(0, 2), 16) / 255];
const c = uicolors.map(col => {
if (col <= 0.03928) { return col / 12.92; }
return (col + 0.055) / 1.055 ** 2.4;
});
const L = (0.2126 * c[0]) + (0.7152 * c[1]) + (0.0722 * c[2]);
return (L > 0.255) ? "#000" : "fff";
})();
}
+23
View File
@@ -33,6 +33,29 @@
animation: spin-loading 2s cubic-bezier(.46,-0.55,.49,1.52) 0s forwards infinite;
}
.glow-on-hover {
border-radius: 10px;
background: linear-gradient(45deg, #ff0000, #ff7300, #fffb00, #48ff00, #00ffd5, #002bff, #7a00ff, #ff00c8, #ff0000);
position: absolute;
top: -4px;
left:-4px;
background-size: 400%;
z-index: -1;
filter: blur(3px);
width: calc(100% + 8px);
height: calc(100% + 8px);
animation: glowing 20s linear infinite;
opacity: 0;
transition: opacity .2s ease-in-out;
border-radius: 10px;
}
@keyframes glowing {
0% { background-position: 0 0; }
50% { background-position: 400% 0; }
100% { background-position: 0 0; }
}
@keyframes spin-loading{
0%{ transform: rotate(-360deg); }
50%{ transform: rotate(-360deg); }
+1 -2
View File
@@ -53,11 +53,10 @@ export class I18nService {
public translate(translationKey: string, args?: Record<string, string>): string{
let translated = this.cache.get(translationKey);
if(!translated){
console.log(translationKey);
translated = getProperty(this.dictionary, translationKey);
translated ? this.cache.set(translated, translationKey) : translated = translationKey;
}
args && Object.keys(args).forEach(key => translated = translated.replaceAll(`{${key}}`, args[key]));
args && Object.keys(args).forEach(key => {translated = translated.replaceAll(`{${key}}`, args[key])});
return translated;
}