Compare commits

...

2 Commits

Author SHA1 Message Date
dwelle
f7a6bffb99 v2 2023-10-02 14:19:25 +02:00
dwelle
ffa4cea61c refactor: FONT_FAMILY and related helpers 2023-08-25 21:05:45 +02:00
26 changed files with 154 additions and 115 deletions

View File

@ -10,13 +10,13 @@ import { FONT_FAMILY } from "@excalidraw/excalidraw";
`FONT_FAMILY` contains all the font families used in `Excalidraw` as explained below `FONT_FAMILY` contains all the font families used in `Excalidraw` as explained below
| Font Family | Description | | Font Family | Description |
| ----------- | ---------------------- | | ------------ | ------------------------------------------- |
| `Virgil` | The `handwritten` font | | `HAND_DRAWN` | The handwritten font (by default, `Virgil`) |
| `Helvetica` | The `Normal` Font | | `NORMAL` | The regular font (by default, `Helvetica`) |
| `Cascadia` | The `Code` Font | | `CODE` | The code font (by default, `Cascadia`) |
Defaults to `FONT_FAMILY.Virgil` unless passed in `initialData.appState.currentItemFontFamily`. Defaults to `HAND_DRAWN` unless passed in `initialData.appState.currentItemFontFamily`.
### THEME ### THEME

View File

@ -111,6 +111,13 @@
type="font/woff2" type="font/woff2"
crossorigin="anonymous" crossorigin="anonymous"
/> />
<link
rel="preload"
href="/VirgilNew2023Beta-Regular.woff2"
as="font"
type="font/woff2"
crossorigin="anonymous"
/>
<link <link
rel="preload" rel="preload"
href="/Cascadia.woff2" href="/Cascadia.woff2"

Binary file not shown.

View File

@ -5,6 +5,12 @@
font-display: swap; font-display: swap;
} }
@font-face {
font-family: "Virgil2";
src: url("VirgilNew2023Beta-Regular.woff2");
font-display: swap;
}
/* https://github.com/microsoft/cascadia-code */ /* https://github.com/microsoft/cascadia-code */
@font-face { @font-face {
font-family: "Cascadia"; font-family: "Cascadia";

View File

@ -10,6 +10,7 @@ import {
computeBoundTextPosition, computeBoundTextPosition,
computeContainerDimensionForBoundText, computeContainerDimensionForBoundText,
getBoundTextElement, getBoundTextElement,
getFontString,
measureText, measureText,
redrawTextBoundingBox, redrawTextBoundingBox,
} from "../element/textElement"; } from "../element/textElement";
@ -31,7 +32,6 @@ import {
} from "../element/types"; } from "../element/types";
import { AppState } from "../types"; import { AppState } from "../types";
import { Mutable } from "../utility-types"; import { Mutable } from "../utility-types";
import { getFontString } from "../utils";
import { register } from "./register"; import { register } from "./register";
export const actionUnbindText = register({ export const actionUnbindText = register({

View File

@ -74,7 +74,7 @@ import {
ExcalidrawElement, ExcalidrawElement,
ExcalidrawLinearElement, ExcalidrawLinearElement,
ExcalidrawTextElement, ExcalidrawTextElement,
FontFamilyValues, FontFamilyId,
TextAlign, TextAlign,
VerticalAlign, VerticalAlign,
} from "../element/types"; } from "../element/types";
@ -689,22 +689,27 @@ export const actionChangeFontFamily = register({
}, },
PanelComponent: ({ elements, appState, updateData }) => { PanelComponent: ({ elements, appState, updateData }) => {
const options: { const options: {
value: FontFamilyValues; value: FontFamilyId;
text: string; text: string;
icon: JSX.Element; icon: JSX.Element;
}[] = [ }[] = [
{ {
value: FONT_FAMILY.Virgil, value: FONT_FAMILY.HAND_DRAWN.fontFamilyId,
text: t("labels.handDrawn"), text: t("labels.handDrawn"),
icon: FreedrawIcon, icon: FreedrawIcon,
}, },
{ {
value: FONT_FAMILY.Helvetica, value: FONT_FAMILY.HAND_DRAWN2.fontFamilyId,
text: t("labels.handDrawn"),
icon: FreedrawIcon,
},
{
value: FONT_FAMILY.NORMAL.fontFamilyId,
text: t("labels.normal"), text: t("labels.normal"),
icon: FontFamilyNormalIcon, icon: FontFamilyNormalIcon,
}, },
{ {
value: FONT_FAMILY.Cascadia, value: FONT_FAMILY.CODE.fontFamilyId,
text: t("labels.code"), text: t("labels.code"),
icon: FontFamilyCodeIcon, icon: FontFamilyCodeIcon,
}, },
@ -713,7 +718,7 @@ export const actionChangeFontFamily = register({
return ( return (
<fieldset> <fieldset>
<legend>{t("labels.fontFamily")}</legend> <legend>{t("labels.fontFamily")}</legend>
<ButtonIconSelect<FontFamilyValues | false> <ButtonIconSelect<FontFamilyId | false>
group="font-family" group="font-family"
options={options} options={options}
value={getFormValue( value={getFormValue(

View File

@ -231,7 +231,6 @@ import {
import { import {
debounce, debounce,
distance, distance,
getFontString,
getNearestScrollableContainer, getNearestScrollableContainer,
isInputLike, isInputLike,
isToolIcon, isToolIcon,
@ -298,6 +297,7 @@ import {
getContainerCenter, getContainerCenter,
getContainerElement, getContainerElement,
getDefaultLineHeight, getDefaultLineHeight,
getFontString,
getLineHeightInPx, getLineHeightInPx,
getTextBindableContainerAtPosition, getTextBindableContainerAtPosition,
isMeasureTextSupported, isMeasureTextSupported,

View File

@ -1,6 +1,6 @@
import cssVariables from "./css/variables.module.scss"; import cssVariables from "./css/variables.module.scss";
import { AppProps } from "./types"; import { AppProps } from "./types";
import { ExcalidrawElement, FontFamilyValues } from "./element/types"; import { ExcalidrawElement, FontFamilyId } from "./element/types";
import { COLOR_PALETTE } from "./colors"; import { COLOR_PALETTE } from "./colors";
export const isDarwin = /Mac|iPod|iPhone|iPad/.test(navigator.platform); export const isDarwin = /Mac|iPod|iPhone|iPad/.test(navigator.platform);
@ -94,10 +94,23 @@ export const CLASSES = {
// 1-based in case we ever do `if(element.fontFamily)` // 1-based in case we ever do `if(element.fontFamily)`
export const FONT_FAMILY = { export const FONT_FAMILY = {
Virgil: 1, HAND_DRAWN: {
Helvetica: 2, fontFamilyId: 1,
Cascadia: 3, fontFamily: "Virgil",
}; },
HAND_DRAWN2: {
fontFamilyId: 4,
fontFamily: "Virgil2",
},
NORMAL: {
fontFamilyId: 2,
fontFamily: "Helvetica",
},
CODE: {
fontFamilyId: 3,
fontFamily: "Cascadia",
},
} as const;
export const THEME = { export const THEME = {
LIGHT: "light", LIGHT: "light",
@ -119,7 +132,8 @@ export const WINDOWS_EMOJI_FALLBACK_FONT = "Segoe UI Emoji";
export const MIN_FONT_SIZE = 1; export const MIN_FONT_SIZE = 1;
export const DEFAULT_FONT_SIZE = 20; export const DEFAULT_FONT_SIZE = 20;
export const DEFAULT_FONT_FAMILY: FontFamilyValues = FONT_FAMILY.Virgil; export const DEFAULT_FONT_FAMILY: FontFamilyId =
FONT_FAMILY.HAND_DRAWN.fontFamilyId;
export const DEFAULT_TEXT_ALIGN = "left"; export const DEFAULT_TEXT_ALIGN = "left";
export const DEFAULT_VERTICAL_ALIGN = "top"; export const DEFAULT_VERTICAL_ALIGN = "top";
export const DEFAULT_VERSION = "{version}"; export const DEFAULT_VERSION = "{version}";

View File

@ -2,7 +2,6 @@ import {
ExcalidrawElement, ExcalidrawElement,
ExcalidrawSelectionElement, ExcalidrawSelectionElement,
ExcalidrawTextElement, ExcalidrawTextElement,
FontFamilyValues,
PointBinding, PointBinding,
StrokeRoundness, StrokeRoundness,
} from "../element/types"; } from "../element/types";
@ -22,11 +21,9 @@ import {
import { isTextElement, isUsingAdaptiveRadius } from "../element/typeChecks"; import { isTextElement, isUsingAdaptiveRadius } from "../element/typeChecks";
import { randomId } from "../random"; import { randomId } from "../random";
import { import {
DEFAULT_FONT_FAMILY,
DEFAULT_TEXT_ALIGN, DEFAULT_TEXT_ALIGN,
DEFAULT_VERTICAL_ALIGN, DEFAULT_VERTICAL_ALIGN,
PRECEDING_ELEMENT_KEY, PRECEDING_ELEMENT_KEY,
FONT_FAMILY,
ROUNDNESS, ROUNDNESS,
DEFAULT_SIDEBAR, DEFAULT_SIDEBAR,
DEFAULT_ELEMENT_PROPS, DEFAULT_ELEMENT_PROPS,
@ -34,12 +31,14 @@ import {
import { getDefaultAppState } from "../appState"; import { getDefaultAppState } from "../appState";
import { LinearElementEditor } from "../element/linearElementEditor"; import { LinearElementEditor } from "../element/linearElementEditor";
import { bumpVersion } from "../element/mutateElement"; import { bumpVersion } from "../element/mutateElement";
import { getFontString, getUpdatedTimestamp, updateActiveTool } from "../utils"; import { getUpdatedTimestamp, updateActiveTool } from "../utils";
import { arrayToMap } from "../utils"; import { arrayToMap } from "../utils";
import { MarkOptional, Mutable } from "../utility-types"; import { MarkOptional, Mutable } from "../utility-types";
import { import {
detectLineHeight, detectLineHeight,
getDefaultLineHeight, getDefaultLineHeight,
getFontFamilyIdByName,
getFontString,
measureBaseline, measureBaseline,
} from "../element/textElement"; } from "../element/textElement";
import { normalizeLink } from "./url"; import { normalizeLink } from "./url";
@ -75,15 +74,6 @@ export type RestoredDataState = {
files: BinaryFiles; files: BinaryFiles;
}; };
const getFontFamilyByName = (fontFamilyName: string): FontFamilyValues => {
if (Object.keys(FONT_FAMILY).includes(fontFamilyName)) {
return FONT_FAMILY[
fontFamilyName as keyof typeof FONT_FAMILY
] as FontFamilyValues;
}
return DEFAULT_FONT_FAMILY;
};
const repairBinding = (binding: PointBinding | null) => { const repairBinding = (binding: PointBinding | null) => {
if (!binding) { if (!binding) {
return null; return null;
@ -186,7 +176,7 @@ const restoreElement = (
element as any element as any
).font.split(" "); ).font.split(" ");
fontSize = parseFloat(fontPx); fontSize = parseFloat(fontPx);
fontFamily = getFontFamilyByName(_fontFamily); fontFamily = getFontFamilyIdByName(_fontFamily);
} }
const text = element.text ?? ""; const text = element.text ?? "";

View File

@ -17,6 +17,7 @@ import {
} from "../element/newElement"; } from "../element/newElement";
import { import {
getDefaultLineHeight, getDefaultLineHeight,
getFontString,
measureText, measureText,
normalizeText, normalizeText,
} from "../element/textElement"; } from "../element/textElement";
@ -33,12 +34,12 @@ import {
ExcalidrawSelectionElement, ExcalidrawSelectionElement,
ExcalidrawTextElement, ExcalidrawTextElement,
FileId, FileId,
FontFamilyValues, FontFamilyId,
TextAlign, TextAlign,
VerticalAlign, VerticalAlign,
} from "../element/types"; } from "../element/types";
import { MarkOptional } from "../utility-types"; import { MarkOptional } from "../utility-types";
import { assertNever, getFontString } from "../utils"; import { assertNever } from "../utils";
export type ValidLinearElement = { export type ValidLinearElement = {
type: "arrow" | "line"; type: "arrow" | "line";
@ -47,7 +48,7 @@ export type ValidLinearElement = {
label?: { label?: {
text: string; text: string;
fontSize?: number; fontSize?: number;
fontFamily?: FontFamilyValues; fontFamily?: FontFamilyId;
textAlign?: TextAlign; textAlign?: TextAlign;
verticalAlign?: VerticalAlign; verticalAlign?: VerticalAlign;
} & MarkOptional<ElementConstructorOpts, "x" | "y">; } & MarkOptional<ElementConstructorOpts, "x" | "y">;
@ -124,7 +125,7 @@ export type ValidContainer =
label?: { label?: {
text: string; text: string;
fontSize?: number; fontSize?: number;
fontFamily?: FontFamilyValues; fontFamily?: FontFamilyId;
textAlign?: TextAlign; textAlign?: TextAlign;
verticalAlign?: VerticalAlign; verticalAlign?: VerticalAlign;
} & MarkOptional<ElementConstructorOpts, "x" | "y">; } & MarkOptional<ElementConstructorOpts, "x" | "y">;

View File

@ -2,9 +2,9 @@ import { register } from "../actions/register";
import { FONT_FAMILY, VERTICAL_ALIGN } from "../constants"; import { FONT_FAMILY, VERTICAL_ALIGN } from "../constants";
import { t } from "../i18n"; import { t } from "../i18n";
import { ExcalidrawProps } from "../types"; import { ExcalidrawProps } from "../types";
import { getFontString, setCursorForShape, updateActiveTool } from "../utils"; import { setCursorForShape, updateActiveTool } from "../utils";
import { newTextElement } from "./newElement"; import { newTextElement } from "./newElement";
import { getContainerElement, wrapText } from "./textElement"; import { getContainerElement, getFontString, wrapText } from "./textElement";
import { isEmbeddableElement } from "./typeChecks"; import { isEmbeddableElement } from "./typeChecks";
import { import {
ExcalidrawElement, ExcalidrawElement,
@ -218,7 +218,7 @@ export const createPlaceholderEmbeddableLabel = (
Math.min(element.width / 2, element.width / text.length), Math.min(element.width / 2, element.width / text.length),
element.width / 30, element.width / 30,
); );
const fontFamily = FONT_FAMILY.Helvetica; const fontFamily = FONT_FAMILY.NORMAL.fontFamilyId;
const fontString = getFontString({ const fontString = getFontString({
fontSize, fontSize,

View File

@ -79,7 +79,7 @@ describe("duplicating single elements", () => {
opacity: 100, opacity: 100,
text: "hello", text: "hello",
fontSize: 20, fontSize: 20,
fontFamily: FONT_FAMILY.Virgil, fontFamily: FONT_FAMILY.HAND_DRAWN.fontFamilyId,
textAlign: "left", textAlign: "left",
verticalAlign: "top", verticalAlign: "top",
}); });

View File

@ -10,17 +10,12 @@ import {
VerticalAlign, VerticalAlign,
Arrowhead, Arrowhead,
ExcalidrawFreeDrawElement, ExcalidrawFreeDrawElement,
FontFamilyValues, FontFamilyId,
ExcalidrawTextContainer, ExcalidrawTextContainer,
ExcalidrawFrameElement, ExcalidrawFrameElement,
ExcalidrawEmbeddableElement, ExcalidrawEmbeddableElement,
} from "../element/types"; } from "../element/types";
import { import { arrayToMap, getUpdatedTimestamp, isTestEnv } from "../utils";
arrayToMap,
getFontString,
getUpdatedTimestamp,
isTestEnv,
} from "../utils";
import { randomInteger, randomId } from "../random"; import { randomInteger, randomId } from "../random";
import { bumpVersion, newElementWith } from "./mutateElement"; import { bumpVersion, newElementWith } from "./mutateElement";
import { getNewGroupIdsForDuplication } from "../groups"; import { getNewGroupIdsForDuplication } from "../groups";
@ -35,6 +30,7 @@ import {
wrapText, wrapText,
getBoundTextMaxWidth, getBoundTextMaxWidth,
getDefaultLineHeight, getDefaultLineHeight,
getFontString,
} from "./textElement"; } from "./textElement";
import { import {
DEFAULT_ELEMENT_PROPS, DEFAULT_ELEMENT_PROPS,
@ -184,7 +180,7 @@ export const newTextElement = (
opts: { opts: {
text: string; text: string;
fontSize?: number; fontSize?: number;
fontFamily?: FontFamilyValues; fontFamily?: FontFamilyId;
textAlign?: TextAlign; textAlign?: TextAlign;
verticalAlign?: VerticalAlign; verticalAlign?: VerticalAlign;
containerId?: ExcalidrawTextContainer["id"] | null; containerId?: ExcalidrawTextContainer["id"] | null;

View File

@ -34,7 +34,6 @@ import {
isTextElement, isTextElement,
} from "./typeChecks"; } from "./typeChecks";
import { mutateElement } from "./mutateElement"; import { mutateElement } from "./mutateElement";
import { getFontString } from "../utils";
import { updateBoundElements } from "./binding"; import { updateBoundElements } from "./binding";
import { import {
TransformHandleType, TransformHandleType,
@ -53,6 +52,7 @@ import {
getApproxMinLineHeight, getApproxMinLineHeight,
measureText, measureText,
getBoundTextMaxHeight, getBoundTextMaxHeight,
getFontString,
} from "./textElement"; } from "./textElement";
import { LinearElementEditor } from "./linearElementEditor"; import { LinearElementEditor } from "./linearElementEditor";

View File

@ -427,6 +427,6 @@ describe("Test getDefaultLineHeight", () => {
}); });
it("should return correct line height", () => { it("should return correct line height", () => {
expect(getDefaultLineHeight(FONT_FAMILY.Cascadia)).toBe(1.2); expect(getDefaultLineHeight(FONT_FAMILY.CODE.fontFamilyId)).toBe(1.2);
}); });
}); });

View File

@ -1,10 +1,10 @@
import { getFontString, arrayToMap, isTestEnv } from "../utils"; import { arrayToMap, isTestEnv } from "../utils";
import { import {
ExcalidrawElement, ExcalidrawElement,
ExcalidrawTextContainer, ExcalidrawTextContainer,
ExcalidrawTextElement, ExcalidrawTextElement,
ExcalidrawTextElementWithContainer, ExcalidrawTextElementWithContainer,
FontFamilyValues, FontFamilyId,
FontString, FontString,
NonDeletedExcalidrawElement, NonDeletedExcalidrawElement,
} from "./types"; } from "./types";
@ -19,6 +19,7 @@ import {
isSafari, isSafari,
TEXT_ALIGN, TEXT_ALIGN,
VERTICAL_ALIGN, VERTICAL_ALIGN,
WINDOWS_EMOJI_FALLBACK_FONT,
} from "../constants"; } from "../constants";
import { MaybeTransformHandleType } from "./transformHandles"; import { MaybeTransformHandleType } from "./transformHandles";
import Scene from "../scene/Scene"; import Scene from "../scene/Scene";
@ -967,17 +968,59 @@ export const isMeasureTextSupported = () => {
const DEFAULT_LINE_HEIGHT = { const DEFAULT_LINE_HEIGHT = {
// ~1.25 is the average for Virgil in WebKit and Blink. // ~1.25 is the average for Virgil in WebKit and Blink.
// Gecko (FF) uses ~1.28. // Gecko (FF) uses ~1.28.
[FONT_FAMILY.Virgil]: 1.25 as ExcalidrawTextElement["lineHeight"], [FONT_FAMILY.HAND_DRAWN.fontFamilyId]:
1.25 as ExcalidrawTextElement["lineHeight"],
[FONT_FAMILY.HAND_DRAWN2.fontFamilyId]:
1.25 as ExcalidrawTextElement["lineHeight"],
// ~1.15 is the average for Virgil in WebKit and Blink. // ~1.15 is the average for Virgil in WebKit and Blink.
// Gecko if all over the place. // Gecko if all over the place.
[FONT_FAMILY.Helvetica]: 1.15 as ExcalidrawTextElement["lineHeight"], [FONT_FAMILY.NORMAL.fontFamilyId]:
1.15 as ExcalidrawTextElement["lineHeight"],
// ~1.2 is the average for Virgil in WebKit and Blink, and kinda Gecko too // ~1.2 is the average for Virgil in WebKit and Blink, and kinda Gecko too
[FONT_FAMILY.Cascadia]: 1.2 as ExcalidrawTextElement["lineHeight"], [FONT_FAMILY.CODE.fontFamilyId]: 1.2 as ExcalidrawTextElement["lineHeight"],
}; };
export const getDefaultLineHeight = (fontFamily: FontFamilyValues) => { export const getDefaultLineHeight = (fontId: number) => {
if (fontFamily in DEFAULT_LINE_HEIGHT) { if (fontId in DEFAULT_LINE_HEIGHT) {
return DEFAULT_LINE_HEIGHT[fontFamily]; return (
DEFAULT_LINE_HEIGHT as Record<number, ExcalidrawTextElement["lineHeight"]>
)[fontId];
} }
return DEFAULT_LINE_HEIGHT[DEFAULT_FONT_FAMILY]; return DEFAULT_LINE_HEIGHT[DEFAULT_FONT_FAMILY];
}; };
export const getFontFamilyIdByName = (fontFamilyName: string): FontFamilyId => {
for (const key in FONT_FAMILY) {
const font = FONT_FAMILY[key as keyof typeof FONT_FAMILY];
if (font.fontFamily === fontFamilyName) {
return font.fontFamilyId;
}
}
return DEFAULT_FONT_FAMILY;
};
export const getFontFamilyString = ({
fontFamily,
}: {
fontFamily: FontFamilyId;
}) => {
for (const key in FONT_FAMILY) {
const font = FONT_FAMILY[key as keyof typeof FONT_FAMILY];
if (font.fontFamilyId === fontFamily) {
return `${font.fontFamily}, ${WINDOWS_EMOJI_FALLBACK_FONT}`;
}
}
return WINDOWS_EMOJI_FALLBACK_FONT;
};
/** returns fontSize+fontFamily string for assignment to DOM elements */
export const getFontString = ({
fontSize,
fontFamily,
}: {
fontSize: number;
fontFamily: FontFamilyId;
}) => {
return `${fontSize}px ${getFontFamilyString({ fontFamily })}` as FontString;
};

View File

@ -798,7 +798,7 @@ describe("textWysiwyg", () => {
await new Promise((r) => setTimeout(r, 0)); await new Promise((r) => setTimeout(r, 0));
updateTextEditor(editor, "Hello World!"); updateTextEditor(editor, "Hello World!");
editor.blur(); editor.blur();
expect(text.fontFamily).toEqual(FONT_FAMILY.Virgil); expect(text.fontFamily).toEqual(FONT_FAMILY.HAND_DRAWN.fontFamilyId);
UI.clickTool("text"); UI.clickTool("text");
mouse.clickAt( mouse.clickAt(
@ -815,7 +815,7 @@ describe("textWysiwyg", () => {
editor.blur(); editor.blur();
expect( expect(
(h.elements[1] as ExcalidrawTextElementWithContainer).fontFamily, (h.elements[1] as ExcalidrawTextElementWithContainer).fontFamily,
).toEqual(FONT_FAMILY.Cascadia); ).toEqual(FONT_FAMILY.CODE.fontFamilyId);
//undo //undo
Keyboard.withModifierKeys({ ctrl: true }, () => { Keyboard.withModifierKeys({ ctrl: true }, () => {
@ -823,7 +823,7 @@ describe("textWysiwyg", () => {
}); });
expect( expect(
(h.elements[1] as ExcalidrawTextElementWithContainer).fontFamily, (h.elements[1] as ExcalidrawTextElementWithContainer).fontFamily,
).toEqual(FONT_FAMILY.Virgil); ).toEqual(FONT_FAMILY.HAND_DRAWN.fontFamilyId);
//redo //redo
Keyboard.withModifierKeys({ ctrl: true, shift: true }, () => { Keyboard.withModifierKeys({ ctrl: true, shift: true }, () => {
@ -831,7 +831,7 @@ describe("textWysiwyg", () => {
}); });
expect( expect(
(h.elements[1] as ExcalidrawTextElementWithContainer).fontFamily, (h.elements[1] as ExcalidrawTextElementWithContainer).fontFamily,
).toEqual(FONT_FAMILY.Cascadia); ).toEqual(FONT_FAMILY.CODE.fontFamilyId);
}); });
it("should wrap text and vertcially center align once text submitted", async () => { it("should wrap text and vertcially center align once text submitted", async () => {
@ -1220,7 +1220,7 @@ describe("textWysiwyg", () => {
expect( expect(
(h.elements[1] as ExcalidrawTextElementWithContainer).fontFamily, (h.elements[1] as ExcalidrawTextElementWithContainer).fontFamily,
).toEqual(FONT_FAMILY.Cascadia); ).toEqual(FONT_FAMILY.CODE.fontFamilyId);
expect(getOriginalContainerHeightFromCache(rectangle.id)).toBe(75); expect(getOriginalContainerHeightFromCache(rectangle.id)).toBe(75);
fireEvent.click(screen.getByTitle(/Very large/i)); fireEvent.click(screen.getByTitle(/Very large/i));
@ -1247,7 +1247,7 @@ describe("textWysiwyg", () => {
fireEvent.click(screen.getByTitle(/code/i)); fireEvent.click(screen.getByTitle(/code/i));
expect( expect(
(h.elements[1] as ExcalidrawTextElementWithContainer).fontFamily, (h.elements[1] as ExcalidrawTextElementWithContainer).fontFamily,
).toEqual(FONT_FAMILY.Cascadia); ).toEqual(FONT_FAMILY.CODE.fontFamilyId);
expect( expect(
(h.elements[1] as ExcalidrawTextElementWithContainer).lineHeight, (h.elements[1] as ExcalidrawTextElementWithContainer).lineHeight,
).toEqual(1.2); ).toEqual(1.2);
@ -1255,7 +1255,7 @@ describe("textWysiwyg", () => {
fireEvent.click(screen.getByTitle(/normal/i)); fireEvent.click(screen.getByTitle(/normal/i));
expect( expect(
(h.elements[1] as ExcalidrawTextElementWithContainer).fontFamily, (h.elements[1] as ExcalidrawTextElementWithContainer).fontFamily,
).toEqual(FONT_FAMILY.Helvetica); ).toEqual(FONT_FAMILY.NORMAL.fontFamilyId);
expect( expect(
(h.elements[1] as ExcalidrawTextElementWithContainer).lineHeight, (h.elements[1] as ExcalidrawTextElementWithContainer).lineHeight,
).toEqual(1.15); ).toEqual(1.15);

View File

@ -1,10 +1,5 @@
import { CODES, KEYS } from "../keys"; import { CODES, KEYS } from "../keys";
import { import { isWritableElement, isTestEnv } from "../utils";
isWritableElement,
getFontString,
getFontFamilyString,
isTestEnv,
} from "../utils";
import Scene from "../scene/Scene"; import Scene from "../scene/Scene";
import { import {
isArrowElement, isArrowElement,
@ -34,6 +29,8 @@ import {
computeContainerDimensionForBoundText, computeContainerDimensionForBoundText,
detectLineHeight, detectLineHeight,
computeBoundTextPosition, computeBoundTextPosition,
getFontString,
getFontFamilyString,
} from "./textElement"; } from "./textElement";
import { import {
actionDecreaseFontSize, actionDecreaseFontSize,

View File

@ -10,8 +10,8 @@ import { MarkNonNullable, ValueOf } from "../utility-types";
export type ChartType = "bar" | "line"; export type ChartType = "bar" | "line";
export type FillStyle = "hachure" | "cross-hatch" | "solid" | "zigzag"; export type FillStyle = "hachure" | "cross-hatch" | "solid" | "zigzag";
export type FontFamilyKeys = keyof typeof FONT_FAMILY; export type FontFamilyId =
export type FontFamilyValues = typeof FONT_FAMILY[FontFamilyKeys]; typeof FONT_FAMILY[keyof typeof FONT_FAMILY]["fontFamilyId"];
export type Theme = typeof THEME[keyof typeof THEME]; export type Theme = typeof THEME[keyof typeof THEME];
export type FontString = string & { _brand: "fontString" }; export type FontString = string & { _brand: "fontString" };
export type GroupId = string; export type GroupId = string;
@ -150,7 +150,7 @@ export type ExcalidrawTextElement = _ExcalidrawElementBase &
Readonly<{ Readonly<{
type: "text"; type: "text";
fontSize: number; fontSize: number;
fontFamily: FontFamilyValues; fontFamily: FontFamilyId;
text: string; text: string;
baseline: number; baseline: number;
textAlign: TextAlign; textAlign: TextAlign;

View File

@ -1,5 +1,6 @@
import { ExcalidrawElementSkeleton } from "../../../data/transform"; import { ExcalidrawElementSkeleton } from "../../../data/transform";
import { FileId } from "../../../element/types"; import { FileId } from "../../../element/types";
import { FONT_FAMILY } from "../entry";
const elements: ExcalidrawElementSkeleton[] = [ const elements: ExcalidrawElementSkeleton[] = [
{ {
@ -39,7 +40,10 @@ const elements: ExcalidrawElementSkeleton[] = [
]; ];
export default { export default {
elements, elements,
appState: { viewBackgroundColor: "#AFEEEE", currentItemFontFamily: 1 }, appState: {
viewBackgroundColor: "#AFEEEE",
currentItemFontFamily: FONT_FAMILY.HAND_DRAWN.fontFamilyId,
},
scrollToContent: true, scrollToContent: true,
libraryItems: [ libraryItems: [
[ [

View File

@ -20,7 +20,7 @@ import type { Drawable } from "roughjs/bin/core";
import type { RoughSVG } from "roughjs/bin/svg"; import type { RoughSVG } from "roughjs/bin/svg";
import { StaticCanvasRenderConfig } from "../scene/types"; import { StaticCanvasRenderConfig } from "../scene/types";
import { distance, getFontString, getFontFamilyString, isRTL } from "../utils"; import { distance, isRTL } from "../utils";
import { getCornerRadius, isPathALoop, isRightAngle } from "../math"; import { getCornerRadius, isPathALoop, isRightAngle } from "../math";
import rough from "roughjs/bin/rough"; import rough from "roughjs/bin/rough";
import { import {
@ -46,6 +46,8 @@ import {
getLineHeightInPx, getLineHeightInPx,
getBoundTextMaxHeight, getBoundTextMaxHeight,
getBoundTextMaxWidth, getBoundTextMaxWidth,
getFontFamilyString,
getFontString,
} from "../element/textElement"; } from "../element/textElement";
import { LinearElementEditor } from "../element/linearElementEditor"; import { LinearElementEditor } from "../element/linearElementEditor";
import { import {

View File

@ -1,8 +1,8 @@
import { isTextElement, refreshTextDimensions } from "../element"; import { isTextElement, refreshTextDimensions } from "../element";
import { newElementWith } from "../element/mutateElement"; import { newElementWith } from "../element/mutateElement";
import { getFontString } from "../element/textElement";
import { isBoundToContainer } from "../element/typeChecks"; import { isBoundToContainer } from "../element/typeChecks";
import { ExcalidrawElement, ExcalidrawTextElement } from "../element/types"; import { ExcalidrawElement, ExcalidrawTextElement } from "../element/types";
import { getFontString } from "../utils";
import type Scene from "./Scene"; import type Scene from "./Scene";
import { ShapeCache } from "./ShapeCache"; import { ShapeCache } from "./ShapeCache";

View File

@ -58,7 +58,7 @@ describe("restoreElements", () => {
const textElement = API.createElement({ const textElement = API.createElement({
type: "text", type: "text",
fontSize: 14, fontSize: 14,
fontFamily: FONT_FAMILY.Virgil, fontFamily: FONT_FAMILY.HAND_DRAWN.fontFamilyId,
text: "text", text: "text",
textAlign: "center", textAlign: "center",
verticalAlign: "middle", verticalAlign: "middle",

View File

@ -666,9 +666,13 @@ describe("regression tests", () => {
it("updates fontSize & fontFamily appState", () => { it("updates fontSize & fontFamily appState", () => {
UI.clickTool("text"); UI.clickTool("text");
expect(h.state.currentItemFontFamily).toEqual(FONT_FAMILY.Virgil); expect(h.state.currentItemFontFamily).toEqual(
FONT_FAMILY.HAND_DRAWN.fontFamilyId,
);
fireEvent.click(screen.getByTitle(/code/i)); fireEvent.click(screen.getByTitle(/code/i));
expect(h.state.currentItemFontFamily).toEqual(FONT_FAMILY.Cascadia); expect(h.state.currentItemFontFamily).toEqual(
FONT_FAMILY.CODE.fontFamilyId,
);
}); });
it("deselects selected element, on pointer up, when click hits element bounding box but doesn't hit the element", () => { it("deselects selected element, on pointer up, when click hits element bounding box but doesn't hit the element", () => {

View File

@ -10,7 +10,7 @@ import {
ExcalidrawBindableElement, ExcalidrawBindableElement,
Arrowhead, Arrowhead,
ChartType, ChartType,
FontFamilyValues, FontFamilyId,
FileId, FileId,
ExcalidrawImageElement, ExcalidrawImageElement,
Theme, Theme,
@ -221,7 +221,7 @@ export type AppState = {
currentItemStrokeStyle: ExcalidrawElement["strokeStyle"]; currentItemStrokeStyle: ExcalidrawElement["strokeStyle"];
currentItemRoughness: number; currentItemRoughness: number;
currentItemOpacity: number; currentItemOpacity: number;
currentItemFontFamily: FontFamilyValues; currentItemFontFamily: FontFamilyId;
currentItemFontSize: number; currentItemFontSize: number;
currentItemTextAlign: TextAlign; currentItemTextAlign: TextAlign;
currentItemStartArrowhead: Arrowhead | null; currentItemStartArrowhead: Arrowhead | null;

View File

@ -4,17 +4,11 @@ import {
CURSOR_TYPE, CURSOR_TYPE,
DEFAULT_VERSION, DEFAULT_VERSION,
EVENT, EVENT,
FONT_FAMILY,
isDarwin, isDarwin,
MIME_TYPES, MIME_TYPES,
THEME, THEME,
WINDOWS_EMOJI_FALLBACK_FONT,
} from "./constants"; } from "./constants";
import { import { NonDeletedExcalidrawElement } from "./element/types";
FontFamilyValues,
FontString,
NonDeletedExcalidrawElement,
} from "./element/types";
import { AppState, DataURL, LastActiveTool, Zoom } from "./types"; import { AppState, DataURL, LastActiveTool, Zoom } from "./types";
import { unstable_batchedUpdates } from "react-dom"; import { unstable_batchedUpdates } from "react-dom";
import { SHAPES } from "./shapes"; import { SHAPES } from "./shapes";
@ -85,30 +79,6 @@ export const isWritableElement = (
(target instanceof HTMLInputElement && (target instanceof HTMLInputElement &&
(target.type === "text" || target.type === "number")); (target.type === "text" || target.type === "number"));
export const getFontFamilyString = ({
fontFamily,
}: {
fontFamily: FontFamilyValues;
}) => {
for (const [fontFamilyString, id] of Object.entries(FONT_FAMILY)) {
if (id === fontFamily) {
return `${fontFamilyString}, ${WINDOWS_EMOJI_FALLBACK_FONT}`;
}
}
return WINDOWS_EMOJI_FALLBACK_FONT;
};
/** returns fontSize+fontFamily string for assignment to DOM elements */
export const getFontString = ({
fontSize,
fontFamily,
}: {
fontSize: number;
fontFamily: FontFamilyValues;
}) => {
return `${fontSize}px ${getFontFamilyString({ fontFamily })}` as FontString;
};
export const debounce = <T extends any[]>( export const debounce = <T extends any[]>(
fn: (...args: T) => void, fn: (...args: T) => void,
timeout: number, timeout: number,