mirror of
https://github.com/muerwre/markdown-home-tab.git
synced 2025-04-24 16:36:41 +07:00
improved settings page
This commit is contained in:
parent
70f6dcd495
commit
06d56d1800
19 changed files with 228 additions and 69 deletions
|
@ -6,5 +6,8 @@
|
|||
"Background": "Background",
|
||||
"Text": "Text",
|
||||
"Links": "Links",
|
||||
"Colors": "Colors"
|
||||
"Colors": "Colors",
|
||||
"Inline code": "Inline code",
|
||||
"Ok": "Ok",
|
||||
"Settings": "Settings"
|
||||
}
|
||||
|
|
|
@ -6,5 +6,8 @@
|
|||
"Background": "Фон",
|
||||
"Text": "Текст",
|
||||
"Links": "Ссылки",
|
||||
"Colors": "Цвета"
|
||||
"Colors": "Цвета",
|
||||
"Inline code": "Код в строке",
|
||||
"Ok": "Ok",
|
||||
"Settings": "Настройки"
|
||||
}
|
||||
|
|
|
@ -6,6 +6,10 @@
|
|||
cursor: pointer;
|
||||
transition: all 0.25s;
|
||||
|
||||
&.size-normal {
|
||||
padding: 4px 20px;
|
||||
}
|
||||
|
||||
&.size-small {
|
||||
padding: 2px 16px;
|
||||
font-size: 0.8rem;
|
||||
|
|
18
src/components/containers/RowGroup/index.tsx
Normal file
18
src/components/containers/RowGroup/index.tsx
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { FC, ReactNode } from "react";
|
||||
import styles from "./styles.module.scss";
|
||||
|
||||
interface RowGroupProps {
|
||||
children: ReactNode[];
|
||||
}
|
||||
|
||||
const RowGroup: FC<RowGroupProps> = ({ children }) => (
|
||||
<div className={styles.group}>
|
||||
{children.map((item, key) => (
|
||||
<div key={key} className={styles.row}>
|
||||
{item}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
export { RowGroup };
|
12
src/components/containers/RowGroup/styles.module.scss
Normal file
12
src/components/containers/RowGroup/styles.module.scss
Normal file
|
@ -0,0 +1,12 @@
|
|||
.group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 6px;
|
||||
box-shadow: var(--color-border) 0 0 0 1px;
|
||||
}
|
||||
|
||||
.row {
|
||||
&:not(:last-child) {
|
||||
box-shadow: var(--color-border) 0 1px;
|
||||
}
|
||||
}
|
15
src/components/containers/SettingsRow/index.tsx
Normal file
15
src/components/containers/SettingsRow/index.tsx
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { FC, PropsWithChildren } from "react";
|
||||
import styles from "./styles.module.scss";
|
||||
|
||||
type SettingsRowProps = PropsWithChildren & {
|
||||
title: string;
|
||||
};
|
||||
|
||||
const SettingsRow: FC<SettingsRowProps> = ({ title, children }) => (
|
||||
<div className={styles.row}>
|
||||
<div className={styles.title}>{title}</div>
|
||||
<div className={styles.item}>{children}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export { SettingsRow };
|
11
src/components/containers/SettingsRow/styles.module.scss
Normal file
11
src/components/containers/SettingsRow/styles.module.scss
Normal file
|
@ -0,0 +1,11 @@
|
|||
.row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 10px;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.title {
|
||||
flex: 1;
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
.editor {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
@ -17,7 +16,7 @@
|
|||
}
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,25 @@
|
|||
import { FC, PropsWithChildren } from "react";
|
||||
import styles from "./styles.module.scss";
|
||||
import { IconButton } from "~/components/buttons/IconButton";
|
||||
|
||||
interface ModalPageProps extends PropsWithChildren {
|
||||
title?: string;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const ModalPage: FC<ModalPageProps> = ({ children }) => (
|
||||
<div className={styles.page}>{children}</div>
|
||||
const ModalPage: FC<ModalPageProps> = ({ children, title, onClose }) => (
|
||||
<div className={styles.page}>
|
||||
<div className={styles.header}>
|
||||
<div className={styles.title}>{title}</div>
|
||||
<div className={styles.close}>
|
||||
<IconButton onClick={onClose} role="button">
|
||||
x
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
export { ModalPage };
|
||||
|
|
|
@ -2,4 +2,16 @@
|
|||
background: var(--color-background);
|
||||
padding: 16px;
|
||||
border-radius: 10px;
|
||||
box-shadow: var(--color-border) 0 0 0 1px;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 0 0 20px 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
flex: 1;
|
||||
font-size: 1.4em;
|
||||
}
|
||||
|
|
70
src/modules/settings/containers/ColorSettings/index.tsx
Normal file
70
src/modules/settings/containers/ColorSettings/index.tsx
Normal file
|
@ -0,0 +1,70 @@
|
|||
import { ChangeEvent, FC, useCallback } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { RowGroup } from "~/components/containers/RowGroup";
|
||||
import { SettingsRow } from "~/components/containers/SettingsRow";
|
||||
import {
|
||||
SettingsValue,
|
||||
useSettings,
|
||||
} from "~/modules/settings/context/SettingsContext";
|
||||
|
||||
const ColorSettings: FC = () => {
|
||||
const { update, settings } = useSettings();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const setString = useCallback(
|
||||
(field: keyof SettingsValue) => (event: ChangeEvent<HTMLInputElement>) => {
|
||||
update({ [field]: event.target.value });
|
||||
},
|
||||
[update]
|
||||
);
|
||||
|
||||
return (
|
||||
<RowGroup>
|
||||
<label htmlFor="color">
|
||||
<SettingsRow title={t("Background")}>
|
||||
<input
|
||||
type="color"
|
||||
id="color"
|
||||
onChange={setString("backgroundColor")}
|
||||
value={settings.backgroundColor}
|
||||
/>
|
||||
</SettingsRow>
|
||||
</label>
|
||||
|
||||
<label htmlFor="color">
|
||||
<SettingsRow title={t("Text")}>
|
||||
<input
|
||||
type="color"
|
||||
id="color"
|
||||
onChange={setString("textColor")}
|
||||
value={settings.textColor}
|
||||
/>
|
||||
</SettingsRow>
|
||||
</label>
|
||||
|
||||
<label htmlFor="color">
|
||||
<SettingsRow title={t("Links")}>
|
||||
<input
|
||||
type="color"
|
||||
id="color"
|
||||
onChange={setString("linkColor")}
|
||||
value={settings.linkColor}
|
||||
/>
|
||||
</SettingsRow>
|
||||
</label>
|
||||
|
||||
<label htmlFor="color">
|
||||
<SettingsRow title={t("Inline code")}>
|
||||
<input
|
||||
type="color"
|
||||
id="color"
|
||||
onChange={setString("codeColor")}
|
||||
value={settings.codeColor}
|
||||
/>
|
||||
</SettingsRow>
|
||||
</label>
|
||||
</RowGroup>
|
||||
);
|
||||
};
|
||||
|
||||
export { ColorSettings };
|
|
@ -1,66 +1,11 @@
|
|||
import { ChangeEvent, FC, useCallback } from "react";
|
||||
import { useSettings } from "../../context/SettingsContext";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FC } from "react";
|
||||
import { ColorSettings } from "../ColorSettings";
|
||||
import styles from "./styles.module.scss";
|
||||
|
||||
const SettingsContainer: FC = () => {
|
||||
const { update, settings } = useSettings();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const updateBackgroundColor = useCallback(
|
||||
(event: ChangeEvent<HTMLInputElement>) => {
|
||||
update({ backgroundColor: event.target.value });
|
||||
},
|
||||
[update]
|
||||
);
|
||||
const updateTextColor = useCallback(
|
||||
(event: ChangeEvent<HTMLInputElement>) => {
|
||||
update({ textColor: event.target.value });
|
||||
},
|
||||
[update]
|
||||
);
|
||||
|
||||
const updateLinkColor = useCallback(
|
||||
(event: ChangeEvent<HTMLInputElement>) => {
|
||||
update({ linkColor: event.target.value });
|
||||
},
|
||||
[update]
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>{t("Colors")}</h2>
|
||||
|
||||
<label htmlFor="color">
|
||||
{t("Background")}
|
||||
<input
|
||||
type="color"
|
||||
id="color"
|
||||
onChange={updateBackgroundColor}
|
||||
value={settings.backgroundColor}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label htmlFor="color">
|
||||
{t("Text")}
|
||||
|
||||
<input
|
||||
type="color"
|
||||
id="color"
|
||||
onChange={updateTextColor}
|
||||
value={settings.textColor}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label htmlFor="color">
|
||||
{t("Links")}
|
||||
|
||||
<input
|
||||
type="color"
|
||||
id="color"
|
||||
onChange={updateLinkColor}
|
||||
value={settings.linkColor}
|
||||
/>
|
||||
</label>
|
||||
<div className={styles.container}>
|
||||
<ColorSettings />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
.container {
|
||||
width: min(100vw, 400px);
|
||||
}
|
|
@ -4,6 +4,7 @@ export interface ColorSettings {
|
|||
backgroundColor: string;
|
||||
textColor: string;
|
||||
linkColor: string;
|
||||
codeColor: string;
|
||||
}
|
||||
|
||||
export type SettingsValue = ColorSettings & {
|
||||
|
@ -15,6 +16,7 @@ export const defaultSettings: SettingsValue = {
|
|||
backgroundColor: "",
|
||||
textColor: "",
|
||||
linkColor: "",
|
||||
codeColor: "",
|
||||
};
|
||||
|
||||
export const SettingsContext = createContext({
|
||||
|
|
|
@ -13,8 +13,12 @@ import {
|
|||
defaultDarkTheme,
|
||||
defaultLightTheme,
|
||||
} from "~/modules/theme/constants/theme";
|
||||
import { Button } from "~/components/buttons/Button";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import styles from "./styles.module.scss";
|
||||
|
||||
const SettingsProvider: FC<PropsWithChildren> = ({ children }) => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useDetectTheme();
|
||||
const defaultColors =
|
||||
theme === Theme.Dark ? defaultDarkTheme : defaultLightTheme;
|
||||
|
@ -36,8 +40,14 @@ const SettingsProvider: FC<PropsWithChildren> = ({ children }) => {
|
|||
<SettingsContext.Provider value={{ settings, update, show, hide }}>
|
||||
{settingsModalVisible && (
|
||||
<Modal onClose={hide}>
|
||||
<ModalPage onClose={hide}>
|
||||
<ModalPage onClose={hide} title={t("Settings")}>
|
||||
<SettingsContainer />
|
||||
|
||||
<div className={styles.buttons}>
|
||||
<div className={styles.filler} />
|
||||
<Button onClick={hide}>{t("Ok")}</Button>
|
||||
<div className={styles.filler} />
|
||||
</div>
|
||||
</ModalPage>
|
||||
</Modal>
|
||||
)}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
.buttons {
|
||||
padding-top: 20px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.filler {
|
||||
flex: 1;
|
||||
}
|
|
@ -9,10 +9,12 @@ export const defaultDarkTheme: ColorSettings = {
|
|||
backgroundColor: "#2e2e2e",
|
||||
textColor: "#eeeeee",
|
||||
linkColor: "#25bfe6",
|
||||
codeColor: "#ff3344",
|
||||
};
|
||||
|
||||
export const defaultLightTheme: ColorSettings = {
|
||||
backgroundColor: "#eeeeee",
|
||||
textColor: "#2e2e2e",
|
||||
linkColor: "#25bfe6",
|
||||
codeColor: "#ff3344",
|
||||
};
|
||||
|
|
|
@ -9,11 +9,22 @@ export const useThemeColors = (settings: ColorSettings) => {
|
|||
|
||||
const border = isDark
|
||||
? background.mix(Color("white"), 0.1)
|
||||
: background.mix(Color("black"), 0.1);
|
||||
: background.mix(Color("black"), 0.2);
|
||||
|
||||
const code = new Color(settings.codeColor);
|
||||
|
||||
document.body.style.setProperty("--color-background", background.hex());
|
||||
document.body.style.setProperty("--color-code", code.hex());
|
||||
document.body.style.setProperty(
|
||||
"--color-code-background",
|
||||
code.fade(isDark ? 0.9 : 0.7).toString()
|
||||
);
|
||||
document.body.style.setProperty("--color-text", settings.textColor);
|
||||
document.body.style.setProperty("--color-link", settings.linkColor);
|
||||
document.body.style.setProperty("--color-border", border.hex());
|
||||
document.body.style.setProperty(
|
||||
"--color-pre-background",
|
||||
isDark ? background.lighten(0.2).hex() : background.darken(0.2).hex()
|
||||
);
|
||||
}, [settings]);
|
||||
};
|
||||
|
|
|
@ -54,3 +54,20 @@ pre {
|
|||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
blockquote {
|
||||
background: var(--color-pre-background);
|
||||
margin-left: 0;
|
||||
padding: 5px 10px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9em;
|
||||
border-left: var(--color-border) 4px solid;
|
||||
|
||||
p {
|
||||
margin: 5px 0;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue