Add font settings
This commit is contained in:
parent
16575b6739
commit
a06d5189de
@ -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"
|
||||
}
|
||||
}
|
@ -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
|
||||
|
19
packages/web/scripts/update-version.js
Executable file
19
packages/web/scripts/update-version.js
Executable 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))
|
@ -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
|
||||
})
|
||||
|
@ -21,7 +21,7 @@ const Header = styled.h2`
|
||||
margin: 0;
|
||||
`
|
||||
const Pre = styled.pre`
|
||||
font-size: 15px;
|
||||
font-size: 1rem;
|
||||
line-height: 1.3em;
|
||||
`
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -176,6 +176,16 @@ options:
|
||||
fr: Verrouillage automatique
|
||||
ja: 自動ロック
|
||||
|
||||
ui_font:
|
||||
en: Interface font
|
||||
fr: Police de l’interface
|
||||
ja: フォント
|
||||
|
||||
monospace:
|
||||
en: Monospace font
|
||||
fr: Police monospace
|
||||
ja: 等幅フォント
|
||||
|
||||
noun:
|
||||
vault:
|
||||
en: vault
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
@ -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, "")
|
||||
|
51
packages/web/src/utils/useEvent.ts
Normal file
51
packages/web/src/utils/useEvent.ts
Normal 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
854
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user