diff --git a/src/actions/actionProperties.tsx b/src/actions/actionProperties.tsx index 45756dca5..e3d28a255 100644 --- a/src/actions/actionProperties.tsx +++ b/src/actions/actionProperties.tsx @@ -13,6 +13,13 @@ import { FillCrossHatchIcon, FillHachureIcon, FillSolidIcon, + FontFamilyCodeIcon, + FontFamilyHandDrawnIcon, + FontFamilyNormalIcon, + FontSizeExtraLargeIcon, + FontSizeLargeIcon, + FontSizeMediumIcon, + FontSizeSmallIcon, SloppinessArchitectIcon, SloppinessArtistIcon, SloppinessCartoonistIcon, @@ -20,18 +27,15 @@ import { StrokeStyleDottedIcon, StrokeStyleSolidIcon, StrokeWidthIcon, - FontSizeSmallIcon, - FontSizeMediumIcon, - FontSizeLargeIcon, - FontSizeExtraLargeIcon, - FontFamilyHandDrawnIcon, - FontFamilyNormalIcon, - FontFamilyCodeIcon, - TextAlignLeftIcon, TextAlignCenterIcon, + TextAlignLeftIcon, TextAlignRightIcon, } from "../components/icons"; -import { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE } from "../constants"; +import { + DEFAULT_FONT_FAMILY, + DEFAULT_FONT_SIZE, + FONT_FAMILY, +} from "../constants"; import { getNonDeletedElements, isTextElement, @@ -44,7 +48,7 @@ import { ExcalidrawElement, ExcalidrawLinearElement, ExcalidrawTextElement, - FontFamily, + FontFamilyValues, TextAlign, } from "../element/types"; import { getLanguage, t } from "../i18n"; @@ -499,19 +503,23 @@ export const actionChangeFontFamily = register({ }; }, PanelComponent: ({ elements, appState, updateData }) => { - const options: { value: FontFamily; text: string; icon: JSX.Element }[] = [ + const options: { + value: FontFamilyValues; + text: string; + icon: JSX.Element; + }[] = [ { - value: 1, + value: FONT_FAMILY.Virgil, text: t("labels.handDrawn"), icon: , }, { - value: 2, + value: FONT_FAMILY.Helvetica, text: t("labels.normal"), icon: , }, { - value: 3, + value: FONT_FAMILY.Cascadia, text: t("labels.code"), icon: , }, @@ -520,7 +528,7 @@ export const actionChangeFontFamily = register({ return (
{t("labels.fontFamily")} - + group="font-family" options={options} value={getFormValue( diff --git a/src/constants.ts b/src/constants.ts index 6dee670b4..89582a550 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,6 +1,6 @@ -import { FontFamily } from "./element/types"; import cssVariables from "./css/variables.module.scss"; import { AppProps } from "./types"; +import { FontFamilyValues } from "./element/types"; export const APP_NAME = "Excalidraw"; @@ -63,15 +63,15 @@ export const CLASSES = { // 1-based in case we ever do `if(element.fontFamily)` export const FONT_FAMILY = { - 1: "Virgil", - 2: "Helvetica", - 3: "Cascadia", -} as const; + Virgil: 1, + Helvetica: 2, + Cascadia: 3, +}; export const WINDOWS_EMOJI_FALLBACK_FONT = "Segoe UI Emoji"; export const DEFAULT_FONT_SIZE = 20; -export const DEFAULT_FONT_FAMILY: FontFamily = 1; +export const DEFAULT_FONT_FAMILY: FontFamilyValues = FONT_FAMILY.Virgil; export const DEFAULT_TEXT_ALIGN = "left"; export const DEFAULT_VERTICAL_ALIGN = "top"; export const DEFAULT_VERSION = "{version}"; diff --git a/src/data/restore.ts b/src/data/restore.ts index 51ef05970..442f052e8 100644 --- a/src/data/restore.ts +++ b/src/data/restore.ts @@ -1,18 +1,18 @@ import { ExcalidrawElement, - FontFamily, ExcalidrawSelectionElement, + FontFamilyValues, } from "../element/types"; import { AppState, NormalizedZoomValue } from "../types"; import { ImportedDataState } from "./types"; -import { isInvisiblySmallElement, getNormalizedDimensions } from "../element"; +import { getNormalizedDimensions, isInvisiblySmallElement } from "../element"; import { isLinearElementType } from "../element/typeChecks"; import { randomId } from "../random"; import { - FONT_FAMILY, DEFAULT_FONT_FAMILY, DEFAULT_TEXT_ALIGN, DEFAULT_VERTICAL_ALIGN, + FONT_FAMILY, } from "../constants"; import { getDefaultAppState } from "../appState"; import { LinearElementEditor } from "../element/linearElementEditor"; @@ -41,11 +41,11 @@ export type RestoredDataState = { appState: RestoredAppState; }; -const getFontFamilyByName = (fontFamilyName: string): FontFamily => { - for (const [id, fontFamilyString] of Object.entries(FONT_FAMILY)) { - if (fontFamilyString.includes(fontFamilyName)) { - return parseInt(id) as FontFamily; - } +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; }; diff --git a/src/element/newElement.test.ts b/src/element/newElement.test.ts index 94826b1fe..2945de84f 100644 --- a/src/element/newElement.test.ts +++ b/src/element/newElement.test.ts @@ -1,6 +1,7 @@ import { duplicateElement } from "./newElement"; import { mutateElement } from "./mutateElement"; import { API } from "../tests/helpers/api"; +import { FONT_FAMILY } from "../constants"; const isPrimitive = (val: any) => { const type = typeof val; @@ -79,7 +80,7 @@ it("clones text element", () => { opacity: 100, text: "hello", fontSize: 20, - fontFamily: 1, + fontFamily: FONT_FAMILY.Virgil, textAlign: "left", verticalAlign: "top", }); diff --git a/src/element/newElement.ts b/src/element/newElement.ts index ac9da794d..0c6f47818 100644 --- a/src/element/newElement.ts +++ b/src/element/newElement.ts @@ -5,11 +5,11 @@ import { ExcalidrawGenericElement, NonDeleted, TextAlign, - FontFamily, GroupId, VerticalAlign, Arrowhead, ExcalidrawFreeDrawElement, + FontFamilyValues, } from "../element/types"; import { measureText, getFontString } from "../utils"; import { randomInteger, randomId } from "../random"; @@ -109,7 +109,7 @@ export const newTextElement = ( opts: { text: string; fontSize: number; - fontFamily: FontFamily; + fontFamily: FontFamilyValues; textAlign: TextAlign; verticalAlign: VerticalAlign; } & ElementConstructorOpts, diff --git a/src/element/types.ts b/src/element/types.ts index ef94a7593..34533e688 100644 --- a/src/element/types.ts +++ b/src/element/types.ts @@ -3,7 +3,8 @@ import { FONT_FAMILY } from "../constants"; export type ChartType = "bar" | "line"; export type FillStyle = "hachure" | "cross-hatch" | "solid"; -export type FontFamily = keyof typeof FONT_FAMILY; +export type FontFamilyKeys = keyof typeof FONT_FAMILY; +export type FontFamilyValues = typeof FONT_FAMILY[FontFamilyKeys]; export type FontString = string & { _brand: "fontString" }; export type GroupId = string; export type PointerType = "mouse" | "pen" | "touch"; @@ -91,7 +92,7 @@ export type ExcalidrawTextElement = _ExcalidrawElementBase & Readonly<{ type: "text"; fontSize: number; - fontFamily: FontFamily; + fontFamily: FontFamilyValues; text: string; baseline: number; textAlign: TextAlign; diff --git a/src/packages/excalidraw/CHANGELOG.md b/src/packages/excalidraw/CHANGELOG.md index d0949271c..0eb239d0e 100644 --- a/src/packages/excalidraw/CHANGELOG.md +++ b/src/packages/excalidraw/CHANGELOG.md @@ -19,6 +19,8 @@ Please add the latest change on the top under the correct section. ### Features +- Expose [`FONT_FAMILY`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#FONT_FAMILY) so that consumer can use when passing `initialData.appState.currentItemFontFamily`. + - Added prop [`autoFocus`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#autoFocus) to focus the excalidraw component on page load when enabled, defaults to false [#3691](https://github.com/excalidraw/excalidraw/pull/3691). Note: Earlier Excalidraw component was focussed by default on page load, you need to enable `autoFocus` prop to retain the same behaviour. diff --git a/src/packages/excalidraw/README_NEXT.md b/src/packages/excalidraw/README_NEXT.md index f1b9382af..df2183a32 100644 --- a/src/packages/excalidraw/README_NEXT.md +++ b/src/packages/excalidraw/README_NEXT.md @@ -840,3 +840,21 @@ This function returns a svg with the exported elements. | exportBackground | boolean | true | Indicates whether background should be exported | | viewBackgroundColor | string | #fff | The default background color | | exportWithDarkMode | boolean | false | Indicates whether to export with dark mode | + +### FONT_FAMILY + +**_Signature_** + +```js +import { FONT_FAMILY } from "./constants"; +``` + +`FONT_FAMILY` contains all the font families used in `Excalidraw` as explained below + +| Font Family | Description | +| ----------- | -------------------- | +| Virgil | The handwritten font | +| Helvetica | The Normal Font | +| Cacadia | The Code Font | + +Defaults to `FONT_FAMILY.Virgil` unless passed in `initialData.appState.currentItemFontFamily`. diff --git a/src/packages/excalidraw/index.tsx b/src/packages/excalidraw/index.tsx index 7fbc00b57..c2d2f74df 100644 --- a/src/packages/excalidraw/index.tsx +++ b/src/packages/excalidraw/index.tsx @@ -180,3 +180,4 @@ export { exportToSvg, } from "../../packages/utils"; export { serializeAsJSON } from "../../data/json"; +export { FONT_FAMILY } from "../../constants"; diff --git a/src/tests/data/restore.test.ts b/src/tests/data/restore.test.ts index 617eb3a20..4c0e8d659 100644 --- a/src/tests/data/restore.test.ts +++ b/src/tests/data/restore.test.ts @@ -1,15 +1,16 @@ import * as restore from "../../data/restore"; import { - ExcalidrawTextElement, + ExcalidrawElement, ExcalidrawFreeDrawElement, ExcalidrawLinearElement, - ExcalidrawElement, + ExcalidrawTextElement, } from "../../element/types"; import * as sizeHelpers from "../../element/sizeHelpers"; import { API } from "../helpers/api"; import { getDefaultAppState } from "../../appState"; import { ImportedDataState } from "../../data/types"; import { NormalizedZoomValue } from "../../types"; +import { FONT_FAMILY } from "../../constants"; const mockSizeHelper = jest.spyOn(sizeHelpers, "isInvisiblySmallElement"); @@ -49,7 +50,7 @@ describe("restoreElements", () => { const textElement = API.createElement({ type: "text", fontSize: 14, - fontFamily: 1, + fontFamily: FONT_FAMILY.Virgil, text: "text", textAlign: "center", verticalAlign: "middle", diff --git a/src/tests/regressionTests.test.tsx b/src/tests/regressionTests.test.tsx index fbfeb3939..ee542f930 100644 --- a/src/tests/regressionTests.test.tsx +++ b/src/tests/regressionTests.test.tsx @@ -15,6 +15,7 @@ import { waitFor, } from "./test-utils"; import { defaultLang } from "../i18n"; +import { FONT_FAMILY } from "../constants"; const { h } = window; @@ -606,9 +607,9 @@ describe("regression tests", () => { it("updates fontSize & fontFamily appState", () => { UI.clickTool("text"); - expect(h.state.currentItemFontFamily).toEqual(1); // Virgil + expect(h.state.currentItemFontFamily).toEqual(FONT_FAMILY.Virgil); fireEvent.click(screen.getByTitle(/code/i)); - expect(h.state.currentItemFontFamily).toEqual(3); // Cascadia + expect(h.state.currentItemFontFamily).toEqual(FONT_FAMILY.Cascadia); }); it("deselects selected element, on pointer up, when click hits element bounding box but doesn't hit the element", () => { diff --git a/src/types.ts b/src/types.ts index 768887176..fb4c05345 100644 --- a/src/types.ts +++ b/src/types.ts @@ -5,11 +5,11 @@ import { NonDeleted, TextAlign, ExcalidrawElement, - FontFamily, GroupId, ExcalidrawBindableElement, Arrowhead, ChartType, + FontFamilyValues, } from "./element/types"; import { SHAPES } from "./shapes"; import { Point as RoughPoint } from "roughjs/bin/geometry"; @@ -68,7 +68,7 @@ export type AppState = { currentItemStrokeStyle: ExcalidrawElement["strokeStyle"]; currentItemRoughness: number; currentItemOpacity: number; - currentItemFontFamily: FontFamily; + currentItemFontFamily: FontFamilyValues; currentItemFontSize: number; currentItemTextAlign: TextAlign; currentItemStrokeSharpness: ExcalidrawElement["strokeSharpness"]; diff --git a/src/utils.ts b/src/utils.ts index 58ce9b28a..915b9bdd1 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -5,7 +5,7 @@ import { FONT_FAMILY, WINDOWS_EMOJI_FALLBACK_FONT, } from "./constants"; -import { FontFamily, FontString } from "./element/types"; +import { FontFamilyValues, FontString } from "./element/types"; import { Zoom } from "./types"; import { unstable_batchedUpdates } from "react-dom"; import { isDarwin } from "./keys"; @@ -71,9 +71,14 @@ export const isWritableElement = ( export const getFontFamilyString = ({ fontFamily, }: { - fontFamily: FontFamily; + fontFamily: FontFamilyValues; }) => { - return `${FONT_FAMILY[fontFamily]}, ${WINDOWS_EMOJI_FALLBACK_FONT}`; + 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 */ @@ -82,7 +87,7 @@ export const getFontString = ({ fontFamily, }: { fontSize: number; - fontFamily: FontFamily; + fontFamily: FontFamilyValues; }) => { return `${fontSize}px ${getFontFamilyString({ fontFamily })}` as FontString; };