Add font settings

This commit is contained in:
aet 2022-02-21 03:54:04 -05:00
parent 16575b6739
commit a06d5189de
14 changed files with 613 additions and 457 deletions

View File

@ -1,6 +1,6 @@
{
"name": "opvault-web",
"version": "1.0.0",
"version": "1.0.220221",
"main": "dist/main/index.js",
"author": "proteria",
"license": "GPL-3.0-or-later",
@ -14,9 +14,10 @@
},
"dependencies": {
"@emotion/css": "^11.7.1",
"@emotion/react": "^11.7.1",
"@emotion/styled": "^11.6.0",
"@emotion/react": "^11.8.1",
"@emotion/styled": "^11.8.1",
"buffer": "^6.0.3",
"lodash-es": "^4.17.21",
"path-browserify": "^1.0.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
@ -24,22 +25,23 @@
"react-idle-timer": "4.6.4"
},
"devDependencies": {
"@babel/core": "^7.16.7",
"@babel/core": "^7.17.5",
"@emotion/babel-plugin": "^11.7.2",
"@rollup/plugin-yaml": "^3.1.0",
"@types/react": "^17.0.37",
"@types/react-dom": "^17.0.11",
"@vitejs/plugin-react": "^1.1.3",
"@types/babel__core": "^7.1.18",
"concurrently": "^6.5.1",
"electron": "^16.0.5",
"electron-builder": "^22.14.5",
"esbuild": "^0.14.5",
"@types/lodash-es": "^4.17.6",
"@types/react": "^17.0.39",
"@types/react-dom": "^17.0.11",
"@vitejs/plugin-react": "^1.2.0",
"concurrently": "^7.0.0",
"electron": "^17.0.1",
"electron-builder": "^22.14.13",
"esbuild": "^0.14.23",
"js-yaml": "^4.1.0",
"lodash": "^4.17.21",
"opvault.js": "*",
"sass": "^1.45.0",
"typescript": "^4.5.4",
"vite": "^2.7.3"
"sass": "^1.49.8",
"typescript": "^4.5.5",
"vite": "^2.8.4"
}
}

View File

@ -1,6 +1,7 @@
#!/bin/sh
./scripts/update-version.js
./scripts/build-i18n-yml-typedef.js
./scripts/build-third-party-license.js
./scripts/build-third-party-license-info.js
./scripts/build-package-json.js
npx vite build
NODE_ENV=production ./esbuild.js

View File

@ -0,0 +1,19 @@
#!/usr/bin/env node
const fs = require("fs")
const { resolve } = require("path")
const json = require("../package.json")
const date = new Date()
json.version = json.version
.split(".")
.slice(0, 2)
.concat(
[
date.getUTCFullYear() - 2000,
(date.getUTCMonth() + 1).toString().padStart(2, "0"),
date.getUTCDate().toString().padStart(2, "0"),
].join("")
)
.join(".")
fs.writeFileSync(resolve(__dirname, "../package.json"), JSON.stringify(json, null, 2))

View File

@ -1,5 +1,11 @@
import { useEffect, memo } from "react"
import { debounce } from "lodash-es"
import { useLocaleContext, useTranslate } from "./i18n"
import { Key, useStorage } from "./utils/localStorage"
const updateCSS = debounce((name: string, value: string) => {
document.body.style.setProperty(name, value || null)
}, 500)
export const SideEffect = memo(() => {
const { locale } = useLocaleContext()
@ -10,5 +16,16 @@ export const SideEffect = memo(() => {
document.title = t.label.app_name
}, [locale])
const [uiFont] = useStorage(Key.UI_FONT)
const [monoFont] = useStorage(Key.MONOSPACE_FONT)
useEffect(() => {
updateCSS("--sans-serif", uiFont)
}, [uiFont])
useEffect(() => {
updateCSS("--monospace", monoFont)
}, [monoFont])
return null
})

View File

@ -21,7 +21,7 @@ const Header = styled.h2`
margin: 0;
`
const Pre = styled.pre`
font-size: 15px;
font-size: 1rem;
line-height: 1.3em;
`

View File

@ -34,7 +34,7 @@ const Separator = styled.div`
const Item = styled.div`
cursor: default;
font-size: 14px;
font-size: 0.875rem;
flex: 1 1 auto;
display: flex;
height: 2.3em;

View File

@ -1,5 +1,6 @@
import styled from "@emotion/styled"
import { useCallback } from "react"
import { useCallback, useEffect, useRef } from "react"
import { useEventListener } from "../utils/useEvent"
const ModalBackground = styled.div`
background: rgba(0, 0, 0, 0.6);
@ -21,11 +22,25 @@ const ModalBackground2 = styled.div`
display: flex;
align-items: center;
`
const ModalContainer = styled.div`
const ModalContainer = styled.dialog`
background: var(--page-background);
box-shadow: rgba(0, 0, 0, 0.25) 0px 14px 28px, rgba(0, 0, 0, 0.22) 0px 10px 10px;
border-radius: 5px;
border: inherit;
box-shadow: rgba(0, 0, 0, 0.25) 0px 14px 28px, rgba(0, 0, 0, 0.22) 0px 10px 10px;
color: inherit;
margin: 0 auto;
padding: 0;
&::backdrop {
background: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(1px);
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
}
`
const ModalTitle = styled.div`
border-bottom: 1px solid var(--border-color);
@ -37,12 +52,15 @@ const ModalContent = styled.div`
padding: 15px 20px;
`
document.createElement("dialog")
export const Modal: React.FC<{
show: boolean
title: string
maxWidth?: number
onClose(): void
}> = ({ show, children, title, maxWidth = 700, onClose }) => {
const dialogRef = useRef<HTMLDialogElement>(null)
const onBackgroundClick = useCallback(
e => {
if (e.currentTarget === e.target) {
@ -53,6 +71,15 @@ export const Modal: React.FC<{
[onClose]
)
useEventListener(document.body, "keyup").on(
e => {
if (show && e.key === "Escape") {
onClose()
}
},
[show]
)
if (!show) {
return null
}
@ -61,7 +88,7 @@ export const Modal: React.FC<{
<>
<ModalBackground />
<ModalBackground2 onClick={onBackgroundClick}>
<ModalContainer style={{ maxWidth }}>
<ModalContainer open ref={dialogRef} style={{ maxWidth }}>
<ModalTitle>{title}</ModalTitle>
<ModalContent>{children}</ModalContent>
</ModalContainer>

View File

@ -176,6 +176,16 @@ options:
fr: Verrouillage automatique
ja: 自動ロック
ui_font:
en: Interface font
fr: Police de linterface
ja: フォント
monospace:
en: Monospace font
fr: Police monospace
ja: 等幅フォント
noun:
vault:
en: vault

View File

@ -10,8 +10,7 @@ body {
margin: 0;
overflow: hidden;
font-size: 15px;
font-family: -apple-system, BlinkMacSystemFont, system-ui, "Roboto", "Oxygen",
"Cantarell", "Droid Sans", "Helvetica Neue", "Noto Sans CJK JP", sans-serif;
font-family: var(--sans-serif);
}
:root {
--page-background: #fff;
@ -22,6 +21,8 @@ body {
--selected-background: #d5d5d5;
--hover-background: #ddd;
--border-color: #e3e3e3;
--sans-serif: -apple-system, BlinkMacSystemFont, system-ui, "Roboto", "Oxygen",
"Cantarell", "Droid Sans", "Helvetica Neue", "Noto Sans CJK JP", sans-serif;
--monospace: D2Coding, "source-code-pro", Menlo, Monaco, Consolas, "Courier New",
monospace;
}
@ -78,7 +79,7 @@ input {
}
input[type="search"],
input[type="input"],
input[type="text"],
input[type="number"],
input[type="password"] {
@include input;
@ -100,7 +101,7 @@ input[type="checkbox" i] {
position: relative;
&:checked:after {
content: "\2714";
font-size: 15px;
font-size: 1rem;
position: absolute;
top: 0px;
left: 3px;
@ -125,7 +126,6 @@ select,
}
}
button {
font-size: 16px;
padding: 8px 15px;
box-shadow: rgb(0 0 0 / 7%) 0px 1px 2px;
transition: 0.1s;

View File

@ -36,7 +36,7 @@ const TabButton = styled.button<{ active?: boolean }>`
box-shadow: none;
display: inline-flex;
margin-bottom: 5px;
font-size: 22px;
font-size: 1.4666em;
padding: 10px 14px;
${p => p.active && "&:hover { background: var(--selected-background); }"}
@media (prefers-color-scheme: dark) {

View File

@ -25,6 +25,9 @@ const Checkbox = styled.input`
margin-left: 0;
margin-right: 8px;
`
const Input = styled.input`
width: 100%;
`
const GhostLabel = styled.div`
opacity: 0.5;
@ -42,6 +45,8 @@ export const Settings: React.FC<{
const [enableAutoLock, setEnableAutoLock] = useStorage(Key.ENABLE_AUTO_LOCK)
const [autolockAfter, setAutolockAfter] = useStorage(Key.AUTO_LOCK_AFTER)
const [uiFont, setUIFont] = useStorage(Key.UI_FONT)
const [monoFont, setMonoFont] = useStorage(Key.MONOSPACE_FONT)
return (
<Modal show={show} title={t.label.settings} onClose={onHide}>
@ -83,6 +88,30 @@ export const Settings: React.FC<{
</GhostLabel>
</FormValue>
</FormItem>
<FormItem>
<FormLabel>{t.options.ui_font}</FormLabel>
<FormValue>
<Input
type="text"
value={uiFont}
onChange={e => setUIFont(e.target.value)}
spellCheck={false}
/>
</FormValue>
</FormItem>
<FormItem>
<FormLabel>{t.options.monospace}</FormLabel>
<FormValue>
<Input
type="text"
value={monoFont}
onChange={e => setMonoFont(e.target.value)}
spellCheck={false}
/>
</FormValue>
</FormItem>
</Modal>
)
}

View File

@ -6,6 +6,8 @@ export enum Key {
PREFERRED_LOCALE = "app.config.locale",
ENABLE_AUTO_LOCK = "app.config.enable_auto_lock",
AUTO_LOCK_AFTER = "app.config.auto_lock_after",
UI_FONT = "app.config.font.ui",
MONOSPACE_FONT = "app.config.font.monospace",
}
interface StoredData {
@ -14,6 +16,8 @@ interface StoredData {
[Key.PREFERRED_LOCALE]: string
[Key.ENABLE_AUTO_LOCK]: boolean
[Key.AUTO_LOCK_AFTER]: number
[Key.UI_FONT]: string
[Key.MONOSPACE_FONT]: string
}
const events = new Map(Object.values(Key).map(key => [key, new Set()])) as {
@ -74,3 +78,5 @@ const defaults: typeof set = (key, value) => {
defaults(Key.ENABLE_AUTO_LOCK, true)
defaults(Key.AUTO_LOCK_AFTER, 180)
defaults(Key.RECENTLY_OPENED_VAULTS, [])
defaults(Key.UI_FONT, "")
defaults(Key.MONOSPACE_FONT, "")

View File

@ -0,0 +1,51 @@
import { useEffect } from "react"
type UseEventListenerOptions = boolean | AddEventListenerOptions
export { useEventListener }
type On<This, Ev> = {
on(listener: (this: This, ev: Ev) => void, deps?: any[]): void
}
interface UseEventListener {
<K extends keyof MediaQueryListEventMap>(
mediaQueryList: MediaQueryList,
type: K,
options?: UseEventListenerOptions
): On<MediaQueryList, MediaQueryListEventMap[K]>
<K extends keyof WindowEventMap>(
window: Window,
type: K,
options?: UseEventListenerOptions
): On<Window, WindowEventMap[K]>
<K extends keyof DocumentEventMap>(
document: Document,
type: K,
options?: UseEventListenerOptions
): On<Document, DocumentEventMap[K]>
<K extends keyof HTMLElementEventMap>(
element: HTMLElement,
type: K,
options?: UseEventListenerOptions
): On<HTMLElement, HTMLElementEventMap[K]>
}
const useEventListener: UseEventListener = function useEventListener(
element: EventTarget,
type: string,
options?: UseEventListenerOptions
) {
return {
on(listener: (ev: any) => any, deps?: any[]) {
// eslint-disable-next-line react-hooks/rules-of-hooks
useEffect(
() => {
element.addEventListener(type, listener, options)
return () => element.removeEventListener(type, listener, options)
},
deps ? [element, type, ...deps] : undefined
)
},
}
}

854
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff