Skip to content

Commit

Permalink
Merge pull request #55 from vimdotmd/feat-size
Browse files Browse the repository at this point in the history
Feat: Calculate editor typography based on size
  • Loading branch information
Thien Do authored Sep 4, 2021
2 parents c615159 + d5d2a6c commit e4d5e8e
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 5 deletions.
2 changes: 2 additions & 0 deletions src/components/editor/editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { PrefsState } from "../prefs/state";
import { useEditorFile } from "./state/file";
import { useEditorInit } from "./state/init/init";
import { useEditorLayout } from "./state/layout";
import { useEditorSize } from "./state/size";
import { EditorState } from "./state/state";
import { useEditorTheme } from "./state/theme/theme";
import { useEditorVim } from "./state/vim";
Expand All @@ -28,6 +29,7 @@ export const Editor = (props: Props): JSX.Element => {
useEditorVim({ editor, prefs, statusRef });
useEditorFile({ editor, file });
useEditorTheme({ editor, prefs });
useEditorSize({ editor, prefs });

return (
<div className={s.container}>
Expand Down
3 changes: 0 additions & 3 deletions src/components/editor/state/layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ export const getEditorLayout = (container: HTMLDivElement): EditorOptions => {
const expected = Math.round(freePadding / 2);
return {
lineDecorationsWidth: Math.max(expected, 48),
fontSize: 18,
lineHeight: 34,
letterSpacing: 0.54, // 0.03 em/%
wordWrapColumn: 64,
};
};
Expand Down
46 changes: 46 additions & 0 deletions src/components/editor/state/size.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { useEffect } from "react";
import { SizeName, SIZE_METRICS } from "~src/components/prefs/size/size";
import { PrefsState } from "~src/components/prefs/state";
import { ThemeName, THEME_COLORS } from "~src/components/prefs/theme/theme";
import { isApple } from "~src/utils/platform";
import { EditorState } from "./state";

interface Params {
editor: EditorState;
prefs: PrefsState;
}

const getWeight = (params: { theme: ThemeName; size: SizeName }): number => {
// Should not have complicated calculation for low dpi screen
if (window.devicePixelRatio === 1) return 400;
// Light text on dark background makes text look bolder
const theme = THEME_COLORS[params.theme];
const darkMod = theme.bg.isDark() ? -20 : 0;
// Apple renders font bolder than Windows
const appleMod = isApple() ? -35 : 0;
// Final value
const { weight } = SIZE_METRICS[params.size];
const final = weight + appleMod + darkMod;
return final;
};

/**
* Update editor font size and line height following user preference
*/
export const useEditorSize = (params: Params): void => {
const editor = params.editor.value;
const size = params.prefs.size;
const theme = params.prefs.theme;

useEffect(() => {
if (editor === null) return;
const metrics = SIZE_METRICS[size];
editor.updateOptions({
fontSize: metrics.fontSize,
lineHeight: Math.round(metrics.fontSize * metrics.lineHeight),
fontWeight: getWeight({ size, theme }).toString(),
// @TODO: Calculate letter spacing using metrics.spacing
// letterSpacing:
});
}, [editor, size, theme]);
};
13 changes: 13 additions & 0 deletions src/components/prefs/prefs.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { SizeName, SIZE_NAMES } from "./size/size";
import { PrefsState } from "./state";
import { ThemeName, THEME_NAMES } from "./theme/theme";

Expand All @@ -7,6 +8,18 @@ interface Props {

export const Prefs = (props: Props): JSX.Element => (
<div>
<select
value={props.prefs.size}
onChange={(event) => {
props.prefs.setSize(event.target.value as SizeName);
}}
>
{SIZE_NAMES.map((size) => (
<option key={size} value={size}>
{size}
</option>
))}
</select>
<select
value={props.prefs.theme}
onChange={(event) => {
Expand Down
50 changes: 50 additions & 0 deletions src/components/prefs/size/size.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
export const SIZE_NAMES = [
"4XS",
"3XS",
"2XS",
"XS",
"S",
"M",
"L",
"XL",
"2XL",
"3XL",
"4XL",
"5XL",
"6XL",
"7XL",
"8XL",
"9XL",
"10XL",
] as const;

export type SizeName = typeof SIZE_NAMES[number];

export interface SizeMetrics {
fontSize: number;
/** Relative to font size */
lineHeight: number;
spacing: number;
/** 400 - 500 */
weight: number;
}

export const SIZE_METRICS: Record<SizeName, SizeMetrics> = {
"4XS": { fontSize: 11, lineHeight: 1.9, spacing: 42, weight: 490 },
"3XS": { fontSize: 12, lineHeight: 1.89, spacing: 38, weight: 482 },
"2XS": { fontSize: 13, lineHeight: 1.89, spacing: 34, weight: 475 },
XS: { fontSize: 14, lineHeight: 1.88, spacing: 28, weight: 470 },
S: { fontSize: 15, lineHeight: 1.87, spacing: 24, weight: 465 },
M: { fontSize: 16, lineHeight: 1.86, spacing: 22, weight: 460 },
L: { fontSize: 18, lineHeight: 1.85, spacing: 20, weight: 455 },
XL: { fontSize: 20, lineHeight: 1.83, spacing: 20, weight: 450 },
"2XL": { fontSize: 24, lineHeight: 1.79, spacing: 20, weight: 445 },
"3XL": { fontSize: 28, lineHeight: 1.75, spacing: 19, weight: 440 },
"4XL": { fontSize: 32, lineHeight: 1.71, spacing: 18, weight: 435 },
"5XL": { fontSize: 36, lineHeight: 1.67, spacing: 17, weight: 430 },
"6XL": { fontSize: 42, lineHeight: 1.61, spacing: 16, weight: 425 },
"7XL": { fontSize: 48, lineHeight: 1.55, spacing: 15, weight: 425 },
"8XL": { fontSize: 54, lineHeight: 1.49, spacing: 14, weight: 425 },
"9XL": { fontSize: 60, lineHeight: 1.43, spacing: 13, weight: 425 },
"10XL": { fontSize: 72, lineHeight: 1.38, spacing: 12, weight: 425 },
};
17 changes: 17 additions & 0 deletions src/components/prefs/size/state.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useStorageState } from "~src/utils/state/storage";
import { SetState } from "~src/utils/state/type";
import { SizeName } from "./size";

export interface SizeState {
size: SizeName;
setSize: SetState<SizeName>;
}

export const usePrefsSize = (): SizeState => {
const [size, setSize] = useStorageState<SizeName>({
storageKey: "size",
defaultValue: "L",
});

return { size, setSize };
};
10 changes: 8 additions & 2 deletions src/components/prefs/state.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import { LayoutState, usePrefsLayout } from "./layout";
import { SizeState, usePrefsSize } from "./size/state";
import { ThemeState, usePrefsTheme } from "./theme/state";
import { VimState, usePrefsVim } from "./vim";

export interface PrefsState extends ThemeState, VimState, LayoutState {}
export interface PrefsState
extends ThemeState,
VimState,
LayoutState,
SizeState {}

export const usePrefs = (): PrefsState => {
const theme = usePrefsTheme();
const vim = usePrefsVim();
const layout = usePrefsLayout();
return { ...theme, ...vim, ...layout };
const size = usePrefsSize();
return { ...theme, ...vim, ...layout, ...size };
};

0 comments on commit e4d5e8e

Please sign in to comment.