diff --git a/packages/pluggableWidgets/rich-text-web/CHANGELOG.md b/packages/pluggableWidgets/rich-text-web/CHANGELOG.md index 21a204b37b..5f2c0c59c6 100644 --- a/packages/pluggableWidgets/rich-text-web/CHANGELOG.md +++ b/packages/pluggableWidgets/rich-text-web/CHANGELOG.md @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Added +- We added support to add custom font families to Rich Text. - We added table support to Rich Text, now it is possible to add tables and configure their layout. ### Fixed diff --git a/packages/pluggableWidgets/rich-text-web/src/RichText.xml b/packages/pluggableWidgets/rich-text-web/src/RichText.xml index 13ccde87c1..b7b4ad71ba 100644 --- a/packages/pluggableWidgets/rich-text-web/src/RichText.xml +++ b/packages/pluggableWidgets/rich-text-web/src/RichText.xml @@ -163,6 +163,22 @@ Enable spell checking + + Custom fonts + + + + Font name + Item + A title for this font combination (e.g., Arial). + + + Font style + Item + The full CSS font-family declaration that will be applied (e.g., arial, helvetica, sans-serif). + + + diff --git a/packages/pluggableWidgets/rich-text-web/src/__tests__/RichText.spec.tsx b/packages/pluggableWidgets/rich-text-web/src/__tests__/RichText.spec.tsx index 5c61e1523c..985627b886 100644 --- a/packages/pluggableWidgets/rich-text-web/src/__tests__/RichText.spec.tsx +++ b/packages/pluggableWidgets/rich-text-web/src/__tests__/RichText.spec.tsx @@ -43,7 +43,8 @@ describe("Rich Text", () => { maxHeightUnit: "none", maxHeight: 0, minHeight: 75, - OverflowY: "auto" + OverflowY: "auto", + customFonts: [] }; }); diff --git a/packages/pluggableWidgets/rich-text-web/src/components/Editor.tsx b/packages/pluggableWidgets/rich-text-web/src/components/Editor.tsx index 959bc33ec5..7a893cd380 100644 --- a/packages/pluggableWidgets/rich-text-web/src/components/Editor.tsx +++ b/packages/pluggableWidgets/rich-text-web/src/components/Editor.tsx @@ -11,9 +11,15 @@ import { useLayoutEffect, useRef } from "react"; -import QuillTableBetter from "../utils/formats/quill-table-better/quill-table-better"; -import "../utils/formats/quill-table-better/assets/css/quill-table-better.scss"; +import { CustomFontsType } from "../../typings/RichTextProps"; +import { EditorDispatchContext } from "../store/EditorProvider"; +import { SET_FULLSCREEN_ACTION } from "../store/store"; import "../utils/customPluginRegisters"; +import { FontStyleAttributor, formatCustomFonts } from "../utils/formats/fonts"; +import "../utils/formats/quill-table-better/assets/css/quill-table-better.scss"; +import QuillTableBetter from "../utils/formats/quill-table-better/quill-table-better"; +import { RESIZE_MODULE_CONFIG } from "../utils/formats/resizeModuleConfig"; +import { ACTION_DISPATCHER } from "../utils/helpers"; import MxQuill from "../utils/MxQuill"; import { enterKeyKeyboardHandler, @@ -24,12 +30,9 @@ import { } from "./CustomToolbars/toolbarHandlers"; import { useEmbedModal } from "./CustomToolbars/useEmbedModal"; import Dialog from "./ModalDialog/Dialog"; -import { RESIZE_MODULE_CONFIG } from "../utils/formats/resizeModuleConfig"; -import { ACTION_DISPATCHER } from "../utils/helpers"; -import { EditorDispatchContext } from "../store/EditorProvider"; -import { SET_FULLSCREEN_ACTION } from "../store/store"; export interface EditorProps { + customFonts: CustomFontsType[]; defaultValue?: string; onTextChange?: (...args: [delta: Delta, oldContent: Delta, source: EmitterSource]) => void; onSelectionChange?: (...args: [range: Range, oldRange: Range, source: EmitterSource]) => void; @@ -42,6 +45,9 @@ export interface EditorProps { // Editor is an uncontrolled React component const Editor = forwardRef((props: EditorProps, ref: MutableRefObject) => { + const fonts = formatCustomFonts(props.customFonts); + const FontStyle = new FontStyleAttributor(fonts); + Quill.register(FontStyle, true); const { theme, defaultValue, style, className, toolbarId, onTextChange, onSelectionChange, readOnly } = props; const containerRef = useRef(null); const modalRef = useRef(null); diff --git a/packages/pluggableWidgets/rich-text-web/src/components/EditorWrapper.tsx b/packages/pluggableWidgets/rich-text-web/src/components/EditorWrapper.tsx index 2152a9610a..c8977ea3b9 100644 --- a/packages/pluggableWidgets/rich-text-web/src/components/EditorWrapper.tsx +++ b/packages/pluggableWidgets/rich-text-web/src/components/EditorWrapper.tsx @@ -185,6 +185,7 @@ function EditorWrapperInner(props: EditorWrapperProps): ReactElement { preset={preset} quill={quillRef.current} toolbarContent={toolbarPreset} + customFonts={props.customFonts} /> {enableStatusBar && ( diff --git a/packages/pluggableWidgets/rich-text-web/src/components/Toolbar.tsx b/packages/pluggableWidgets/rich-text-web/src/components/Toolbar.tsx index d3ba938665..cc21aec182 100644 --- a/packages/pluggableWidgets/rich-text-web/src/components/Toolbar.tsx +++ b/packages/pluggableWidgets/rich-text-web/src/components/Toolbar.tsx @@ -1,15 +1,17 @@ import classNames from "classnames"; import Quill from "quill"; import { CSSProperties, ReactElement, RefObject, createElement, forwardRef } from "react"; -import { PresetEnum } from "typings/RichTextProps"; +import { CustomFontsType, PresetEnum } from "../../typings/RichTextProps"; +import { formatCustomFonts } from "../utils/formats/fonts"; import { FormatsContainer, ToolbarContext, presetToNumberConverter } from "./CustomToolbars/ToolbarWrapper"; import { TOOLBAR_MAPPING, toolbarContentType } from "./CustomToolbars/constants"; export interface ToolbarProps { + customFonts?: CustomFontsType[]; id: string; preset: PresetEnum; - style?: CSSProperties; quill?: Quill | null; + style?: CSSProperties; toolbarContent: toolbarContentType[]; } @@ -65,6 +67,16 @@ const Toolbar = forwardRef((props: ToolbarProps, ref: RefObject) {toolbarGroup.children.map((toolbar, idx) => { const currentToolbar = TOOLBAR_MAPPING[toolbar]; const key = `toolbar_${id}_${index}_${idx}`; + let value = currentToolbar.value; + + if (currentToolbar.title === "Font type") { + type FontListType = Array<{ value: string; description: string; style: string }>; + value = [ + ...(currentToolbar.value as FontListType), + ...formatCustomFonts(props.customFonts ?? []) + ].sort((a, b) => (a.value ?? "").localeCompare(b.value ?? "")); + } + return currentToolbar.custom ? createElement(currentToolbar.component, { key, @@ -77,7 +89,7 @@ const Toolbar = forwardRef((props: ToolbarProps, ref: RefObject) key, className: classNames(currentToolbar.className), presetValue: currentToolbar.presetValue, - value: currentToolbar.value, + value, title: currentToolbar.title }, currentToolbar.children && createElement(currentToolbar.children) diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/fonts.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/fonts.ts index 3a520cd6ba..6b3e445bc0 100644 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/fonts.ts +++ b/packages/pluggableWidgets/rich-text-web/src/utils/formats/fonts.ts @@ -1,5 +1,5 @@ import { Scope, StyleAttributor } from "parchment"; -import Quill from "quill"; +import { CustomFontsType } from "../../../typings/RichTextProps"; import "./fonts.scss"; export const FONT_LIST = [ @@ -22,13 +22,21 @@ const config = { scope: Scope.INLINE }; -class FontStyleAttributor extends StyleAttributor { +export class FontStyleAttributor extends StyleAttributor { + private fontList: typeof FONT_LIST = []; + + constructor(fontList: typeof FONT_LIST) { + super("font", "font-family", config); + this.fontList = fontList; + } + add(node: HTMLElement, value: any): boolean { if (!this.canAdd(node, value)) { return false; } node.dataset.value = value; - const style = FONT_LIST.find(x => x.value === value)?.style; + const allFonts = [...FONT_LIST, ...this.fontList]; + const style = allFonts.find(x => x.value === value)?.style; if (style) { super.add(node, style); } else { @@ -46,6 +54,10 @@ class FontStyleAttributor extends StyleAttributor { } } -const FontStyle = new FontStyleAttributor("font", "font-family", config); - -Quill.register(FontStyle, true); +export function formatCustomFonts(fonts: CustomFontsType[] = []): typeof FONT_LIST { + return fonts.map(font => ({ + value: font.fontName?.toLowerCase().split(" ").join("-") ?? "", + description: font.fontName ?? "", + style: font.fontStyle ?? "" + })); +} diff --git a/packages/pluggableWidgets/rich-text-web/typings/RichTextProps.d.ts b/packages/pluggableWidgets/rich-text-web/typings/RichTextProps.d.ts index 0380ae4691..7aa3da5485 100644 --- a/packages/pluggableWidgets/rich-text-web/typings/RichTextProps.d.ts +++ b/packages/pluggableWidgets/rich-text-web/typings/RichTextProps.d.ts @@ -23,6 +23,11 @@ export type OverflowYEnum = "auto" | "scroll" | "hidden"; export type OnChangeTypeEnum = "onLeave" | "onDataChange"; +export interface CustomFontsType { + fontName: string; + fontStyle: string; +} + export type ToolbarConfigEnum = "basic" | "advanced"; export type CtItemTypeEnum = "separator" | "undo" | "redo" | "bold" | "italic" | "underline" | "strike" | "superScript" | "subScript" | "orderedList" | "bulletList" | "lowerAlphaList" | "checkList" | "minIndent" | "plusIndent" | "direction" | "link" | "image" | "video" | "formula" | "blockquote" | "code" | "codeBlock" | "viewCode" | "align" | "centerAlign" | "rightAlign" | "font" | "size" | "color" | "background" | "header" | "fullscreen" | "clean" | "tableBetter"; @@ -31,6 +36,11 @@ export interface AdvancedConfigType { ctItemType: CtItemTypeEnum; } +export interface CustomFontsPreviewType { + fontName: string; + fontStyle: string; +} + export interface AdvancedConfigPreviewType { ctItemType: CtItemTypeEnum; } @@ -59,6 +69,7 @@ export interface RichTextContainerProps { onLoad?: ActionValue; onChangeType: OnChangeTypeEnum; spellCheck: boolean; + customFonts: CustomFontsType[]; toolbarConfig: ToolbarConfigEnum; history: boolean; fontStyle: boolean; @@ -100,6 +111,7 @@ export interface RichTextPreviewProps { onLoad: {} | null; onChangeType: OnChangeTypeEnum; spellCheck: boolean; + customFonts: CustomFontsPreviewType[]; toolbarConfig: ToolbarConfigEnum; history: boolean; fontStyle: boolean;