mirror of
https://github.com/Vomitblood/stort.git
synced 2024-11-26 13:55:27 +08:00
style improvements
This commit is contained in:
parent
72f9121593
commit
e48611b11c
|
@ -1,7 +1,8 @@
|
||||||
import { Close, UnfoldLess, UnfoldMore } from "@mui/icons-material";
|
import { Close, UnfoldLess, UnfoldMore } from "@mui/icons-material";
|
||||||
import { Box, IconButton, Modal, Tooltip, Typography } from "@mui/material";
|
import { Box, IconButton, Modal, Tooltip, Typography, useTheme } from "@mui/material";
|
||||||
import { FC, ReactNode } from "react";
|
import { FC, ReactNode } from "react";
|
||||||
import { useSettings } from "../../contexts/SettingsContext";
|
import { useSettings } from "../../contexts/SettingsContext";
|
||||||
|
import { hexToRgba } from "../../lib/utils/color";
|
||||||
|
|
||||||
interface FloatingDialog {
|
interface FloatingDialog {
|
||||||
actionButtons?: ReactNode;
|
actionButtons?: ReactNode;
|
||||||
|
@ -28,7 +29,11 @@ export const FloatingDialog: FC<FloatingDialog> = ({
|
||||||
title,
|
title,
|
||||||
sx,
|
sx,
|
||||||
}) => {
|
}) => {
|
||||||
|
// contexts
|
||||||
const { settings } = useSettings();
|
const { settings } = useSettings();
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
|
const { r, g, b, a } = hexToRgba(theme.palette.background.paper);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -40,7 +45,8 @@ export const FloatingDialog: FC<FloatingDialog> = ({
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
bgcolor: "background.paper",
|
backdropFilter: `blur(${settings.style.blur_radius}px)`,
|
||||||
|
backgroundColor: `rgba(${r}, ${g}, ${b}, ${settings.style.opacity})`,
|
||||||
borderRadius: maximisedState ? "0px" : settings.style.radius + "px",
|
borderRadius: maximisedState ? "0px" : settings.style.radius + "px",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Box, Button } from "@mui/material";
|
import { Box, Button, useTheme } from "@mui/material";
|
||||||
import { convertFileSrc } from "@tauri-apps/api/tauri";
|
import { convertFileSrc } from "@tauri-apps/api/tauri";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useSettings } from "../../contexts/SettingsContext";
|
import { useSettings } from "../../contexts/SettingsContext";
|
||||||
|
@ -6,7 +6,9 @@ import { FooterBar } from "../FooterBar/FooterBar";
|
||||||
import { HeaderBar } from "../HeaderBar/HeaderBar";
|
import { HeaderBar } from "../HeaderBar/HeaderBar";
|
||||||
|
|
||||||
export const Layout = () => {
|
export const Layout = () => {
|
||||||
|
// contexts
|
||||||
const { settings } = useSettings();
|
const { settings } = useSettings();
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
const [imageUrl, setImageUrl] = useState<string | null>(null);
|
const [imageUrl, setImageUrl] = useState<string | null>(null);
|
||||||
|
|
||||||
|
@ -23,7 +25,7 @@ export const Layout = () => {
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
// Use the URL function for background images
|
// Use the URL function for background images
|
||||||
backgroundColor: settings.background.background_color,
|
backgroundColor: theme.palette.background.default,
|
||||||
backgroundImage: settings.background.background_image_path ? `url(${imageUrl})` : "",
|
backgroundImage: settings.background.background_image_path ? `url(${imageUrl})` : "",
|
||||||
backgroundSize: "cover",
|
backgroundSize: "cover",
|
||||||
backgroundPosition: "center",
|
backgroundPosition: "center",
|
||||||
|
|
|
@ -7,9 +7,9 @@ import { useEffect, useState } from "react";
|
||||||
import { useSettings } from "../../../contexts/SettingsContext";
|
import { useSettings } from "../../../contexts/SettingsContext";
|
||||||
import { stagedSettingsAtom } from "../../../lib/store/jotai/settings";
|
import { stagedSettingsAtom } from "../../../lib/store/jotai/settings";
|
||||||
import { FloatingDialog } from "../../Generic/FloatingDialog";
|
import { FloatingDialog } from "../../Generic/FloatingDialog";
|
||||||
import { SettingsTabBackground } from "./SettingsTabs/SettingsTabBackground";
|
import { Background } from "./SettingsTabs/Background";
|
||||||
import { SettingsTabStyle } from "./SettingsTabs/SettingsTabStyle";
|
import { Style } from "./SettingsTabs/Style";
|
||||||
import { SettingsTabWindow } from "./SettingsTabs/SettingsTabWindow";
|
import { Window } from "./SettingsTabs/Window";
|
||||||
|
|
||||||
export const Settings = () => {
|
export const Settings = () => {
|
||||||
// contexts
|
// contexts
|
||||||
|
@ -127,19 +127,19 @@ export const Settings = () => {
|
||||||
sx={{ p: 2 }}
|
sx={{ p: 2 }}
|
||||||
value="style"
|
value="style"
|
||||||
>
|
>
|
||||||
<SettingsTabStyle />
|
<Style />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel
|
<TabPanel
|
||||||
sx={{ p: 2 }}
|
sx={{ p: 2 }}
|
||||||
value="background"
|
value="background"
|
||||||
>
|
>
|
||||||
<SettingsTabBackground />
|
<Background />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel
|
<TabPanel
|
||||||
sx={{ p: 2 }}
|
sx={{ p: 2 }}
|
||||||
value="window"
|
value="window"
|
||||||
>
|
>
|
||||||
<SettingsTabWindow />
|
<Window />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</Box>
|
</Box>
|
||||||
</TabContext>
|
</TabContext>
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
import { DeleteOutline, FileOpenOutlined } from "@mui/icons-material";
|
import { DeleteOutline, FileOpenOutlined } from "@mui/icons-material";
|
||||||
import { Box, Button, Stack, TextField } from "@mui/material";
|
import { Box, Button, Stack, TextField } from "@mui/material";
|
||||||
import { open } from "@tauri-apps/api/dialog";
|
import { open } from "@tauri-apps/api/dialog";
|
||||||
import { convertFileSrc, invoke } from "@tauri-apps/api/tauri";
|
import { readBinaryFile } from "@tauri-apps/api/fs";
|
||||||
|
import { invoke } from "@tauri-apps/api/tauri";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { FC, useEffect, useState } from "react";
|
import { FC, useEffect, useState } from "react";
|
||||||
import { useSettings } from "../../../../contexts/SettingsContext";
|
import { useSettings } from "../../../../contexts/SettingsContext";
|
||||||
|
import { defaultSettings } from "../../../../lib/settings";
|
||||||
import { stagedSettingsAtom } from "../../../../lib/store/jotai/settings";
|
import { stagedSettingsAtom } from "../../../../lib/store/jotai/settings";
|
||||||
import { CategoryTitle } from "../CategoryTitle";
|
import { CategoryTitle } from "../CategoryTitle";
|
||||||
import { SettingsItem } from "../SettingsItem";
|
import { SettingsItem } from "../SettingsItem";
|
||||||
import { readBinaryFile } from "@tauri-apps/api/fs";
|
|
||||||
|
|
||||||
interface SettingsTabBackgroundProps {
|
interface BackgroundProps {
|
||||||
sx?: any;
|
sx?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SettingsTabBackground: FC<SettingsTabBackgroundProps> = ({ sx }) => {
|
export const Background: FC<BackgroundProps> = ({ sx }) => {
|
||||||
// contexts
|
// contexts
|
||||||
const { settings, updateSettings } = useSettings();
|
const { settings, updateSettings } = useSettings();
|
||||||
|
|
||||||
|
@ -195,7 +196,7 @@ export const SettingsTabBackground: FC<SettingsTabBackgroundProps> = ({ sx }) =>
|
||||||
</Box>
|
</Box>
|
||||||
<CategoryTitle title="Colors" />
|
<CategoryTitle title="Colors" />
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
defaultText="#202124"
|
defaultText={defaultSettings.background.background_color}
|
||||||
description="Background color"
|
description="Background color"
|
||||||
input={
|
input={
|
||||||
<TextField
|
<TextField
|
||||||
|
@ -213,13 +214,25 @@ export const SettingsTabBackground: FC<SettingsTabBackgroundProps> = ({ sx }) =>
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<button
|
<SettingsItem
|
||||||
onClick={() => {
|
defaultText={defaultSettings.background.background_color_popup}
|
||||||
updateSettings(stagedSettings);
|
description="Popup background color"
|
||||||
}}
|
input={
|
||||||
>
|
<TextField
|
||||||
apply
|
name="background_color_popup"
|
||||||
</button>
|
onChange={(e) => {
|
||||||
|
handleSettingsBackgroundValueChange(e.target.name, e.target.value);
|
||||||
|
}}
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
size="small"
|
||||||
|
type="color"
|
||||||
|
value={stagedSettings.background.background_color_popup}
|
||||||
|
variant="standard"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
|
@ -1,17 +1,18 @@
|
||||||
import { Box, InputAdornment, MenuItem, Slider, Switch, TextField, Typography } from "@mui/material";
|
import { Box, InputAdornment, MenuItem, Slider, Switch, TextField } from "@mui/material";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
|
import { defaultSettings } from "../../../../lib/settings";
|
||||||
import { stagedSettingsAtom } from "../../../../lib/store/jotai/settings";
|
import { stagedSettingsAtom } from "../../../../lib/store/jotai/settings";
|
||||||
import { BetaChip } from "../BetaChip";
|
import { BetaChip } from "../BetaChip";
|
||||||
import { CategoryTitle } from "../CategoryTitle";
|
import { CategoryTitle } from "../CategoryTitle";
|
||||||
import { DevChip } from "../DevChip";
|
import { DevChip } from "../DevChip";
|
||||||
import { SettingsItem } from "../SettingsItem";
|
import { SettingsItem } from "../SettingsItem";
|
||||||
|
|
||||||
interface SettingsTabStyleProps {
|
interface StyleProps {
|
||||||
sx?: any;
|
sx?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SettingsTabStyle: FC<SettingsTabStyleProps> = ({ sx }) => {
|
export const Style: FC<StyleProps> = ({ sx }) => {
|
||||||
// atoms
|
// atoms
|
||||||
const [stagedSettings, setStagedSettings] = useAtom(stagedSettingsAtom);
|
const [stagedSettings, setStagedSettings] = useAtom(stagedSettingsAtom);
|
||||||
|
|
||||||
|
@ -32,7 +33,7 @@ export const SettingsTabStyle: FC<SettingsTabStyleProps> = ({ sx }) => {
|
||||||
<Box sx={{ sx }}>
|
<Box sx={{ sx }}>
|
||||||
<CategoryTitle title="Basic styles" />
|
<CategoryTitle title="Basic styles" />
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
defaultText="On"
|
defaultText={defaultSettings.style.dark_mode ? "On" : "Off"}
|
||||||
description={
|
description={
|
||||||
<>
|
<>
|
||||||
<BetaChip />
|
<BetaChip />
|
||||||
|
@ -50,7 +51,7 @@ export const SettingsTabStyle: FC<SettingsTabStyleProps> = ({ sx }) => {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
defaultText="#8ab4f8"
|
defaultText={defaultSettings.style.accent_color}
|
||||||
description="Accent color"
|
description="Accent color"
|
||||||
input={
|
input={
|
||||||
<TextField
|
<TextField
|
||||||
|
@ -69,7 +70,7 @@ export const SettingsTabStyle: FC<SettingsTabStyleProps> = ({ sx }) => {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
defaultText="Monospace"
|
defaultText={defaultSettings.style.font_family}
|
||||||
description="Font family"
|
description="Font family"
|
||||||
inputLong={
|
inputLong={
|
||||||
<TextField
|
<TextField
|
||||||
|
@ -94,7 +95,7 @@ export const SettingsTabStyle: FC<SettingsTabStyleProps> = ({ sx }) => {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
defaultText="100%"
|
defaultText={defaultSettings.style.font_scaling + "%"}
|
||||||
description="Font scaling"
|
description="Font scaling"
|
||||||
inputBottom={
|
inputBottom={
|
||||||
<Slider
|
<Slider
|
||||||
|
@ -134,20 +135,20 @@ export const SettingsTabStyle: FC<SettingsTabStyleProps> = ({ sx }) => {
|
||||||
/>
|
/>
|
||||||
<CategoryTitle title="Advanced settings" />
|
<CategoryTitle title="Advanced settings" />
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
defaultText="200ms"
|
defaultText={defaultSettings.style.blur_radius + "px"}
|
||||||
description={
|
description={
|
||||||
<>
|
<>
|
||||||
<DevChip />
|
<DevChip />
|
||||||
Transition duration
|
Blur radius
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
input={
|
input={
|
||||||
<TextField
|
<TextField
|
||||||
InputProps={{
|
InputProps={{
|
||||||
endAdornment: <InputAdornment position="end">ms</InputAdornment>,
|
endAdornment: <InputAdornment position="end">px</InputAdornment>,
|
||||||
inputProps: { min: 0, max: 100, step: 1 },
|
inputProps: { min: 0, max: 100, step: 1 },
|
||||||
}}
|
}}
|
||||||
name="transition_duration"
|
name="blur_radius"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
handleSettingsStyleValueChange(e.target.name, parseFloat(e.target.value));
|
handleSettingsStyleValueChange(e.target.name, parseFloat(e.target.value));
|
||||||
}}
|
}}
|
||||||
|
@ -156,13 +157,13 @@ export const SettingsTabStyle: FC<SettingsTabStyleProps> = ({ sx }) => {
|
||||||
}}
|
}}
|
||||||
size="small"
|
size="small"
|
||||||
type="number"
|
type="number"
|
||||||
value={stagedSettings.style.transition_duration}
|
value={stagedSettings.style.blur_radius}
|
||||||
variant="standard"
|
variant="standard"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
defaultText="8px"
|
defaultText={defaultSettings.style.radius + "px"}
|
||||||
description={
|
description={
|
||||||
<>
|
<>
|
||||||
<DevChip />
|
<DevChip />
|
||||||
|
@ -190,7 +191,62 @@ export const SettingsTabStyle: FC<SettingsTabStyleProps> = ({ sx }) => {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
defaultText="60%"
|
defaultText={defaultSettings.style.opacity.toString()}
|
||||||
|
description={
|
||||||
|
<>
|
||||||
|
<DevChip />
|
||||||
|
Opacity
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
input={
|
||||||
|
<TextField
|
||||||
|
InputProps={{
|
||||||
|
inputProps: { min: 0, max: 1, step: 0.01 },
|
||||||
|
}}
|
||||||
|
name="opacity"
|
||||||
|
onChange={(e) => {
|
||||||
|
handleSettingsStyleValueChange(e.target.name, parseFloat(e.target.value));
|
||||||
|
}}
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
size="small"
|
||||||
|
type="number"
|
||||||
|
value={stagedSettings.style.opacity}
|
||||||
|
variant="standard"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<SettingsItem
|
||||||
|
defaultText={defaultSettings.style.transition_duration.toString()}
|
||||||
|
description={
|
||||||
|
<>
|
||||||
|
<DevChip />
|
||||||
|
Transition duration
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
input={
|
||||||
|
<TextField
|
||||||
|
InputProps={{
|
||||||
|
endAdornment: <InputAdornment position="end">ms</InputAdornment>,
|
||||||
|
inputProps: { min: 0, max: 100, step: 1 },
|
||||||
|
}}
|
||||||
|
name="transition_duration"
|
||||||
|
onChange={(e) => {
|
||||||
|
handleSettingsStyleValueChange(e.target.name, parseFloat(e.target.value));
|
||||||
|
}}
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
size="small"
|
||||||
|
type="number"
|
||||||
|
value={stagedSettings.style.transition_duration}
|
||||||
|
variant="standard"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<SettingsItem
|
||||||
|
defaultText={defaultSettings.style.window_height + "%"}
|
||||||
description={
|
description={
|
||||||
<>
|
<>
|
||||||
<DevChip />
|
<DevChip />
|
||||||
|
@ -218,7 +274,7 @@ export const SettingsTabStyle: FC<SettingsTabStyleProps> = ({ sx }) => {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
defaultText="400px"
|
defaultText={defaultSettings.style.window_width + "px"}
|
||||||
description={
|
description={
|
||||||
<>
|
<>
|
||||||
<DevChip />
|
<DevChip />
|
|
@ -4,12 +4,13 @@ import { FC } from "react";
|
||||||
import { stagedSettingsAtom } from "../../../../lib/store/jotai/settings";
|
import { stagedSettingsAtom } from "../../../../lib/store/jotai/settings";
|
||||||
import { CategoryTitle } from "../CategoryTitle";
|
import { CategoryTitle } from "../CategoryTitle";
|
||||||
import { SettingsItem } from "../SettingsItem";
|
import { SettingsItem } from "../SettingsItem";
|
||||||
|
import { defaultSettings } from "../../../../lib/settings";
|
||||||
|
|
||||||
interface SettingsTabWindowProps {
|
interface WindowProps {
|
||||||
sx?: any;
|
sx?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SettingsTabWindow: FC<SettingsTabWindowProps> = ({ sx }) => {
|
export const Window: FC<WindowProps> = ({ sx }) => {
|
||||||
// atoms
|
// atoms
|
||||||
const [stagedSettings, setStagedSettings] = useAtom(stagedSettingsAtom);
|
const [stagedSettings, setStagedSettings] = useAtom(stagedSettingsAtom);
|
||||||
|
|
||||||
|
@ -30,7 +31,7 @@ export const SettingsTabWindow: FC<SettingsTabWindowProps> = ({ sx }) => {
|
||||||
<Box sx={{ sx }}>
|
<Box sx={{ sx }}>
|
||||||
<CategoryTitle title="Fullscreen" />
|
<CategoryTitle title="Fullscreen" />
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
defaultText="On"
|
defaultText={defaultSettings.window.start_fullscreen ? "On" : "Off"}
|
||||||
description="Fullscreen on startup"
|
description="Fullscreen on startup"
|
||||||
input={
|
input={
|
||||||
<Switch
|
<Switch
|
||||||
|
@ -44,7 +45,7 @@ export const SettingsTabWindow: FC<SettingsTabWindowProps> = ({ sx }) => {
|
||||||
/>
|
/>
|
||||||
<CategoryTitle title="Titlebar Buttons" />
|
<CategoryTitle title="Titlebar Buttons" />
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
defaultText="Off"
|
defaultText={defaultSettings.window.minimize_button ? "On" : "Off"}
|
||||||
description="Minimize button"
|
description="Minimize button"
|
||||||
input={
|
input={
|
||||||
<Switch
|
<Switch
|
||||||
|
@ -57,7 +58,7 @@ export const SettingsTabWindow: FC<SettingsTabWindowProps> = ({ sx }) => {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
defaultText="Off"
|
defaultText={defaultSettings.window.maximize_button ? "On" : "Off"}
|
||||||
description="Maximize button"
|
description="Maximize button"
|
||||||
input={
|
input={
|
||||||
<Switch
|
<Switch
|
|
@ -60,8 +60,8 @@ export const UserThemeProvider: FC<UserThemeProviderProps> = ({ children }) => {
|
||||||
A700: "#616161",
|
A700: "#616161",
|
||||||
},
|
},
|
||||||
background: {
|
background: {
|
||||||
paper: settings.style.dark_mode ? "#303134" : "#fff",
|
paper: settings.style.dark_mode ? settings.background.background_color_popup : "#fff",
|
||||||
default: settings.style.dark_mode ? "#202124" : "#fff",
|
default: settings.style.dark_mode ? settings.background.background_color : "#fff",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,13 +4,16 @@ import { readTomlFile, writeTomlFile } from "./utils/toml";
|
||||||
export const defaultSettings = {
|
export const defaultSettings = {
|
||||||
background: {
|
background: {
|
||||||
background_color: "#202124" as string,
|
background_color: "#202124" as string,
|
||||||
|
background_color_popup: "#303134" as string,
|
||||||
background_image_path: "" as string,
|
background_image_path: "" as string,
|
||||||
},
|
},
|
||||||
style: {
|
style: {
|
||||||
accent_color: "#8ab4f8" as string,
|
accent_color: "#8ab4f8" as string,
|
||||||
|
blur_radius: 10 as number,
|
||||||
dark_mode: true as boolean,
|
dark_mode: true as boolean,
|
||||||
font_family: "monospace" as string,
|
font_family: "monospace" as string,
|
||||||
font_scaling: 100,
|
font_scaling: 100,
|
||||||
|
opacity: 0.8 as number,
|
||||||
radius: 8 as number,
|
radius: 8 as number,
|
||||||
transition_duration: 200 as number,
|
transition_duration: 200 as number,
|
||||||
window_height: 80 as number,
|
window_height: 80 as number,
|
||||||
|
|
26
src/lib/utils/color.ts
Normal file
26
src/lib/utils/color.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
export const hexToRgba = (hex: string): { r: number; g: number; b: number; a: number } => {
|
||||||
|
// remove the hash at the start if it is there
|
||||||
|
hex = hex.replace("#", "");
|
||||||
|
|
||||||
|
let r, g, b, a;
|
||||||
|
|
||||||
|
if (hex.length === 8) {
|
||||||
|
// hex have alpha (rrggbbaa)
|
||||||
|
r = parseInt(hex.substring(0, 2), 16);
|
||||||
|
g = parseInt(hex.substring(2, 4), 16);
|
||||||
|
b = parseInt(hex.substring(4, 6), 16);
|
||||||
|
// convert alpha (from 0-255) to decimal (from 0-1)
|
||||||
|
a = parseInt(hex.substring(6, 8), 16) / 255;
|
||||||
|
} else if (hex.length === 6) {
|
||||||
|
// hex no have alpha
|
||||||
|
r = parseInt(hex.substring(0, 2), 16);
|
||||||
|
g = parseInt(hex.substring(2, 4), 16);
|
||||||
|
b = parseInt(hex.substring(4, 6), 16);
|
||||||
|
// default alpha is 1
|
||||||
|
a = 1;
|
||||||
|
} else {
|
||||||
|
throw new Error("Invalid hex color format. Must be #rrggbbaa or #rrggbb.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return { r, g, b, a };
|
||||||
|
};
|
Loading…
Reference in a new issue