Jelajahi Sumber

feat: expose fontfamily and refactor FONT_FAMILY (#3710)

* feat: expose fontfamily and refactor FONT_FAMILY for better readability

* fix

* fix

* fix

* docs

* fix
Aakansha Doshi 3 tahun lalu
induk
melakukan
6dee02e320

+ 23 - 15
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: <FontFamilyHandDrawnIcon theme={appState.theme} />,
       },
       {
-        value: 2,
+        value: FONT_FAMILY.Helvetica,
         text: t("labels.normal"),
         icon: <FontFamilyNormalIcon theme={appState.theme} />,
       },
       {
-        value: 3,
+        value: FONT_FAMILY.Cascadia,
         text: t("labels.code"),
         icon: <FontFamilyCodeIcon theme={appState.theme} />,
       },
@@ -520,7 +528,7 @@ export const actionChangeFontFamily = register({
     return (
       <fieldset>
         <legend>{t("labels.fontFamily")}</legend>
-        <ButtonIconSelect<FontFamily | false>
+        <ButtonIconSelect<FontFamilyValues | false>
           group="font-family"
           options={options}
           value={getFormValue(

+ 6 - 6
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}";

+ 8 - 8
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;
 };

+ 2 - 1
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",
   });

+ 2 - 2
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,

+ 3 - 2
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;

+ 2 - 0
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.

+ 18 - 0
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`.

+ 1 - 0
src/packages/excalidraw/index.tsx

@@ -180,3 +180,4 @@ export {
   exportToSvg,
 } from "../../packages/utils";
 export { serializeAsJSON } from "../../data/json";
+export { FONT_FAMILY } from "../../constants";

+ 4 - 3
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",

+ 3 - 2
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", () => {

+ 2 - 2
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"];

+ 9 - 4
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;
 };