diff --git a/apps/mobile/app/screens/editor/tiptap/types.ts b/apps/mobile/app/screens/editor/tiptap/types.ts
index aab228f71..a9e3d3026 100644
--- a/apps/mobile/app/screens/editor/tiptap/types.ts
+++ b/apps/mobile/app/screens/editor/tiptap/types.ts
@@ -48,6 +48,8 @@ export type Settings = {
keyboardShown?: boolean;
doubleSpacedLines?: boolean;
corsProxy: string;
+ fontSize: string;
+ fontFamily: string;
};
export type EditorProps = {
diff --git a/apps/mobile/app/screens/editor/tiptap/use-editor-events.ts b/apps/mobile/app/screens/editor/tiptap/use-editor-events.ts
index b290bab48..959528946 100644
--- a/apps/mobile/app/screens/editor/tiptap/use-editor-events.ts
+++ b/apps/mobile/app/screens/editor/tiptap/use-editor-events.ts
@@ -136,6 +136,13 @@ export const useEditorEvents = (
const doubleSpacedLines = useSettingStore(
(state) => state.settings?.doubleSpacedLines
);
+ const defaultFontSize = useSettingStore(
+ (state) => state.settings.defaultFontSize
+ );
+ const defaultFontFamily = useSettingStore(
+ (state) => state.settings.defaultFontFamily
+ );
+
const tools = useDragState((state) => state.data);
useEffect(() => {
@@ -164,7 +171,9 @@ export const useEditorEvents = (
noHeader: noHeader,
noToolbar: readonly || editorPropReadonly || noToolbar,
doubleSpacedLines: doubleSpacedLines,
- corsProxy: corsProxy
+ corsProxy: corsProxy,
+ fontSize: defaultFontSize,
+ fontFamily: defaultFontFamily
});
}, [
fullscreen,
@@ -179,7 +188,9 @@ export const useEditorEvents = (
editorPropReadonly,
noHeader,
noToolbar,
- corsProxy
+ corsProxy,
+ defaultFontSize,
+ defaultFontFamily
]);
const onBackPress = useCallback(async () => {
diff --git a/apps/mobile/app/screens/settings/components.tsx b/apps/mobile/app/screens/settings/components.tsx
index da09823b2..3109e6c92 100644
--- a/apps/mobile/app/screens/settings/components.tsx
+++ b/apps/mobile/app/screens/settings/components.tsx
@@ -26,6 +26,7 @@ import { Licenses } from "./licenses";
import SoundPicker from "./sound-picker";
import { Subscription } from "./subscription";
import { TrashIntervalSelector } from "./trash-interval-selector";
+import { FontSelector } from "./font-selector";
export const components: { [name: string]: ReactElement } = {
colorpicker: ,
homeselector: ,
@@ -35,5 +36,6 @@ export const components: { [name: string]: ReactElement } = {
"debug-logs": ,
"sound-picker": ,
licenses: ,
- "trash-interval-selector":
+ "trash-interval-selector": ,
+ "font-selector":
};
diff --git a/apps/mobile/app/screens/settings/font-selector.jsx b/apps/mobile/app/screens/settings/font-selector.jsx
new file mode 100644
index 000000000..e25697e07
--- /dev/null
+++ b/apps/mobile/app/screens/settings/font-selector.jsx
@@ -0,0 +1,110 @@
+/*
+This file is part of the Notesnook project (https://notesnook.com/)
+
+Copyright (C) 2023 Streetwriters (Private) Limited
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+import React, { useRef, useState } from "react";
+import { View } from "react-native";
+import Menu, { MenuItem } from "react-native-reanimated-material-menu";
+import Icon from "react-native-vector-icons/MaterialCommunityIcons";
+import { PressableButton } from "../../components/ui/pressable";
+import Paragraph from "../../components/ui/typography/paragraph";
+import SettingsService from "../../services/settings";
+import { useSettingStore } from "../../stores/use-setting-store";
+import { useThemeStore } from "../../stores/use-theme-store";
+import { SIZE } from "../../utils/size";
+import { getFontById, getFonts } from "@notesnook/editor/dist/utils/font";
+
+export const FontSelector = () => {
+ const colors = useThemeStore((state) => state.colors);
+ const defaultFontFamily = useSettingStore(
+ (state) => state.settings.defaultFontFamily
+ );
+ const menuRef = useRef();
+ const [width, setWidth] = useState(0);
+
+ const onChange = (item) => {
+ menuRef.current?.hide();
+ SettingsService.set({
+ defaultFontFamily: item
+ });
+ };
+ return (
+ {
+ setWidth(event.nativeEvent.layout.width);
+ }}
+ style={{
+ width: "100%"
+ }}
+ >
+
+
+ );
+};
diff --git a/apps/mobile/app/screens/settings/section-item.tsx b/apps/mobile/app/screens/settings/section-item.tsx
index 53ac29af8..aab39a1c0 100644
--- a/apps/mobile/app/screens/settings/section-item.tsx
+++ b/apps/mobile/app/screens/settings/section-item.tsx
@@ -37,6 +37,7 @@ import { useThemeStore } from "../../stores/use-theme-store";
import { SIZE } from "../../utils/size";
import { components } from "./components";
import { RouteParams, SettingSection } from "./types";
+import { IconButton } from "../../components/ui/icon-button";
const _SectionItem = ({ item }: { item: SettingSection }) => {
const colors = useThemeStore((state) => state.colors);
@@ -64,6 +65,12 @@ const _SectionItem = ({ item }: { item: SettingSection }) => {
backgroundColor: colors.errorBg
}
: {};
+
+ const updateInput = (value: any) => {
+ inputRef?.current?.setNativeProps({
+ text: value + ""
+ });
+ };
return isHidden ? null : (
{
defaultValue={item.inputProperties?.defaultValue}
/>
)}
+
+ {item.type === "input-selector" && (
+
+ {
+ const rawValue = SettingsService.get()[
+ item.property as keyof SettingStore["settings"]
+ ] as string;
+ if (rawValue) {
+ const currentValue = parseInt(rawValue);
+ if (currentValue <= 0) return;
+ const nextValue = currentValue - 1;
+ SettingsService.set({
+ [item.property as string]: nextValue
+ });
+ updateInput(nextValue);
+ }
+ }}
+ size={SIZE.xl}
+ />
+ {
+ if (e.nativeEvent.text) {
+ SettingsService.set({
+ [item.property as string]: e.nativeEvent.text
+ });
+ }
+ item.inputProperties?.onSubmitEditing?.(e);
+ }}
+ onChangeText={(text) => {
+ if (text) {
+ if (item.minInputValue) {
+ text =
+ parseInt(text) < item.minInputValue
+ ? item.minInputValue + ""
+ : text;
+ }
+ SettingsService.set({
+ [item.property as string]: text
+ });
+ }
+ item.inputProperties?.onSubmitEditing?.(text as any);
+ }}
+ keyboardType="decimal-pad"
+ containerStyle={{
+ width: 45
+ }}
+ wrapperStyle={{
+ maxWidth: 45,
+ marginBottom: 0,
+ marginHorizontal: 6
+ }}
+ fwdRef={inputRef}
+ onLayout={() => {
+ if (item.property) {
+ updateInput(SettingsService.get()[item.property]);
+ }
+ }}
+ defaultValue={item.inputProperties?.defaultValue}
+ />
+ {
+ const rawValue = SettingsService.get()[
+ item.property as keyof SettingStore["settings"]
+ ] as string;
+ if (rawValue) {
+ const currentValue = parseInt(rawValue);
+ const nextValue = currentValue + 1;
+ SettingsService.set({
+ [item.property as string]: nextValue
+ });
+ updateInput(nextValue);
+ }
+ }}
+ size={SIZE.xl}
+ />
+
+ )}
diff --git a/apps/mobile/app/screens/settings/settings-data.tsx b/apps/mobile/app/screens/settings/settings-data.tsx
index badfbc490..9080d3dc5 100644
--- a/apps/mobile/app/screens/settings/settings-data.tsx
+++ b/apps/mobile/app/screens/settings/settings-data.tsx
@@ -18,6 +18,7 @@ along with this program. If not, see .
*/
import notifee from "@notifee/react-native";
+
import dayjs from "dayjs";
import React from "react";
import { Linking, Platform } from "react-native";
@@ -450,13 +451,13 @@ export const settingsGroups: SettingSection[] = [
},
{
id: "customize",
- name: "Customize",
+ name: "Customization",
sections: [
{
id: "personalization",
type: "screen",
name: "Theme",
- description: "Change app look and feel",
+ description: "Change app look and feel with color themes",
icon: "shape",
sections: [
{
@@ -514,7 +515,7 @@ export const settingsGroups: SettingSection[] = [
id: "behaviour",
type: "screen",
name: "Behaviour",
- description: "Change app homepage",
+ description: "Change how the app behaves in different situations",
sections: [
{
id: "default-home",
@@ -532,6 +533,63 @@ export const settingsGroups: SettingSection[] = [
component: "trash-interval-selector"
}
]
+ },
+ {
+ id: "editor",
+ name: "Editor",
+ type: "screen",
+ icon: "note-edit-outline",
+ description: "Customize the editor to fit your needs",
+ sections: [
+ {
+ id: "configure-toolbar",
+ type: "screen",
+ name: "Configure toolbar",
+ description: "Make the toolbar adaptable to your needs.",
+ component: "configuretoolbar"
+ },
+ {
+ id: "reset-toolbar",
+ name: "Reset toolbar",
+ description: "Reset toolbar configuration to default",
+ modifer: () => {
+ useDragState.getState().setPreset("default");
+ }
+ },
+ {
+ id: "double-spaced-lines",
+ name: "Use double spaced lines",
+ description:
+ "New lines will be double spaced (old ones won't be affected).",
+ type: "switch",
+ property: "doubleSpacedLines",
+ icon: "format-line-spacing",
+ onChange: () => {
+ ToastEvent.show({
+ heading: "Line spacing changed",
+ type: "success"
+ });
+ }
+ },
+ {
+ id: "default-font-size",
+ name: "Default font size",
+ description: "Set the default font size in editor",
+ type: "input-selector",
+ minInputValue: 8,
+ icon: "format-size",
+ property: "defaultFontSize"
+ },
+ {
+ id: "default-font-family",
+ name: "Default font family",
+ description: "Set the default font family in editor",
+ type: "component",
+ icon: "format-font",
+ property: "defaultFontFamily",
+ component: "font-selector"
+ }
+ ]
}
]
},
@@ -928,42 +986,6 @@ export const settingsGroups: SettingSection[] = [
}
]
},
- {
- id: "editor",
- name: "Editor",
- sections: [
- {
- id: "configure-toolbar",
- type: "screen",
- name: "Configure toolbar",
- description: "Make the toolbar adaptable to your needs.",
- component: "configuretoolbar"
- },
- {
- id: "reset-toolbar",
- name: "Reset toolbar",
- description: "Reset toolbar configuration to default",
- modifer: () => {
- useDragState.getState().setPreset("default");
- }
- },
- {
- id: "double-spaced-lines",
- name: "Use double spaced lines",
- description:
- "New lines will be double spaced (old ones won't be affected).",
- type: "switch",
- property: "doubleSpacedLines",
- icon: "format-line-spacing",
- onChange: () => {
- ToastEvent.show({
- heading: "Line spacing changed",
- type: "success"
- });
- }
- }
- ]
- },
{
id: "help-support",
name: "Help and support",
diff --git a/apps/mobile/app/screens/settings/types.ts b/apps/mobile/app/screens/settings/types.ts
index 70c1720fd..80e95db90 100644
--- a/apps/mobile/app/screens/settings/types.ts
+++ b/apps/mobile/app/screens/settings/types.ts
@@ -22,7 +22,14 @@ import { Settings } from "../../stores/use-setting-store";
export type SettingSection = {
id: string;
- type?: "screen" | "switch" | "component" | "danger" | "input";
+ type?:
+ | "screen"
+ | "switch"
+ | "component"
+ | "danger"
+ | "input"
+ | "input-selector"
+ | "dropdown-selector";
name?: string | ((current?: unknown) => string);
description?: string | ((current: unknown) => string);
icon?: string;
@@ -35,6 +42,8 @@ export type SettingSection = {
hidden?: (current: unknown) => boolean;
onChange?: (property: boolean) => void;
inputProperties?: TextInput["props"];
+ options?: any[];
+ minInputValue?: number;
};
export type SettingsGroup = {
diff --git a/apps/mobile/app/stores/use-navigation-store.ts b/apps/mobile/app/stores/use-navigation-store.ts
index 37fdcefc4..b87aaa4b0 100644
--- a/apps/mobile/app/stores/use-navigation-store.ts
+++ b/apps/mobile/app/stores/use-navigation-store.ts
@@ -69,6 +69,7 @@ export type RouteParams = {
AppLock: AppLockRouteParams;
Auth: AuthParams;
Reminders: GenericRouteParam;
+ SettingsGroup: GenericRouteParam;
};
export type RouteName = keyof RouteParams;
diff --git a/apps/mobile/app/stores/use-setting-store.ts b/apps/mobile/app/stores/use-setting-store.ts
index f98721fbf..704f3b681 100644
--- a/apps/mobile/app/stores/use-setting-store.ts
+++ b/apps/mobile/app/stores/use-setting-store.ts
@@ -73,6 +73,8 @@ export type Settings = {
corsProxy: string;
disableRealtimeSync?: boolean;
notificationSound?: Sound & { platform: PlatformOSType };
+ defaultFontSize: string;
+ defaultFontFamily: string;
};
type DimensionsType = {
@@ -149,7 +151,9 @@ export const useSettingStore = create((set) => ({
defaultSnoozeTime: "5",
corsProxy: "https://cors.notesnook.com",
reminderNotificationMode: "urgent",
- notificationSound: undefined
+ notificationSound: undefined,
+ defaultFontFamily: "sans-serif",
+ defaultFontSize: "16"
},
sheetKeyboardHandler: true,
fullscreen: false,
diff --git a/apps/mobile/app/utils/index.js b/apps/mobile/app/utils/index.js
index 1fd892d4b..517c397e7 100755
--- a/apps/mobile/app/utils/index.js
+++ b/apps/mobile/app/utils/index.js
@@ -27,7 +27,6 @@ import { db } from "../common/database";
import { tabBarRef } from "./global-refs";
let prevTarget = null;
-let htmlToText;
export const TOOLTIP_POSITIONS = {
LEFT: 1,
@@ -171,3 +170,8 @@ export function showTooltip(event, text, position = 2) {
clickToHide: true
});
}
+
+export function toTitleCase(value) {
+ if (!value) return;
+ return value.slice(0, 1).toUpperCase() + value.slice(1);
+}
diff --git a/apps/web/src/components/dropdown-button/index.js b/apps/web/src/components/dropdown-button/index.js
index a88d91048..8fe76e1f3 100644
--- a/apps/web/src/components/dropdown-button/index.js
+++ b/apps/web/src/components/dropdown-button/index.js
@@ -21,14 +21,19 @@ import { Button, Flex } from "@theme-ui/components";
import { useMenuTrigger } from "../../hooks/use-menu";
import { ChevronDown } from "../icons";
-export default function DropdownButton({ title, options }) {
+export default function DropdownButton(props) {
const { openMenu } = useMenuTrigger();
+ const { options, title, sx, buttonStyle, chevronStyle } = props;
if (!options || !options.length) return null;
return (
-
+
diff --git a/apps/web/src/components/editor/title-box.tsx b/apps/web/src/components/editor/title-box.tsx
index 733a10cb5..a2a207958 100644
--- a/apps/web/src/components/editor/title-box.tsx
+++ b/apps/web/src/components/editor/title-box.tsx
@@ -23,6 +23,8 @@ import { useStore, store } from "../../stores/editor-store";
import { debounceWithId } from "../../utils/debounce";
import useMobile from "../../hooks/use-mobile";
import useTablet from "../../hooks/use-tablet";
+import { useEditorConfig } from "./context";
+import { getFontById } from "@notesnook/editor";
type TitleBoxProps = {
readonly: boolean;
@@ -35,6 +37,7 @@ function TitleBox(props: TitleBoxProps) {
const id = useStore((store) => store.session.id);
const isMobile = useMobile();
const isTablet = useTablet();
+ const { editorConfig } = useEditorConfig();
const MAX_FONT_SIZE = useMemo(() => {
return isMobile || isTablet ? 1.625 : 2.625;
@@ -66,7 +69,7 @@ function TitleBox(props: TitleBoxProps) {
readOnly={readonly}
sx={{
p: 0,
- fontFamily: "heading",
+ fontFamily: getFontById(editorConfig.fontFamily)?.font || "heading",
fontSize: ["1.625em", "1.625em", "2.625em"],
fontWeight: "heading",
width: "100%"
diff --git a/apps/web/src/views/settings.js b/apps/web/src/views/settings.js
index 60cfffad5..2a67db0b9 100644
--- a/apps/web/src/views/settings.js
+++ b/apps/web/src/views/settings.js
@@ -18,7 +18,7 @@ along with this program. If not, see .
*/
import { useEffect, useMemo, useState } from "react";
-import { Button, Flex, Text } from "@theme-ui/components";
+import { Button, Flex, Input, Text } from "@theme-ui/components";
import * as Icon from "../components/icons";
import { useStore as useUserStore } from "../stores/user-store";
import { useStore as useNoteStore } from "../stores/note-store";
@@ -81,6 +81,8 @@ import { useTelemetry } from "../hooks/use-telemetry";
import useSpellChecker from "../hooks/use-spell-checker";
import useDesktopIntegration from "../hooks/use-desktop-integration";
import { writeText } from "clipboard-polyfill";
+import { useEditorConfig } from "../components/editor/context";
+import { getFonts } from "@notesnook/editor";
function subscriptionStatusToString(user) {
const status = user?.subscription?.type;
@@ -219,6 +221,7 @@ function Settings() {
"corsProxy",
"https://cors.notesnook.com"
);
+ const { editorConfig, setEditorConfig } = useEditorConfig();
useEffect(() => {
(async () => {
@@ -622,6 +625,51 @@ function Settings() {
/>
{groups.editor && (
<>
+ ({
+ value: font.id,
+ title: font.title
+ }))}
+ onSelectionChanged={(option) => {
+ setEditorConfig({ fontFamily: option.value });
+ }}
+ />
+
+
+ {
+ setEditorConfig({ fontSize: e.currentTarget.valueAsNumber });
+ }}
+ />
+
+ {/* {}}
+ onIncrease={() => {}}
+ title="Set default font size"
+ onReset={() => setEditorConfig({ fontFamily: "16px" })}
+ /> */}
+ {/* */}
.
import {
Editor,
+ getFontById,
PortalProvider,
Toolbar,
usePermissionHandler,
@@ -227,6 +228,7 @@ const Tiptap = ({
readonly={settings.readonly}
controller={controllerRef}
title={controller.title}
+ fontFamily={settings.fontFamily}
/>
>
@@ -234,6 +236,8 @@ const Tiptap = ({
@@ -254,6 +258,8 @@ const Tiptap = ({
editor={_editor}
location="bottom"
tools={[...settings.tools]}
+ defaultFontFamily={settings.fontFamily}
+ defaultFontSize={settings.fontSize}
/>
)}
@@ -262,7 +268,10 @@ const Tiptap = ({
};
const ContentDiv = memo(
- forwardRef((props, ref) => {
+ forwardRef<
+ HTMLDivElement,
+ { padding: number; fontSize: number; fontFamily: string }
+ >((props, ref) => {
const theme = useEditorThemeStore((state) => state.colors);
return (
);
}),
- () => true
+ (prev, next) => {
+ if (prev.fontSize !== next.fontSize || prev.fontFamily !== next.fontFamily)
+ return false;
+ return true;
+ }
);
const modifyToolbarTheme = (toolbarTheme: Theme) => {
diff --git a/packages/editor-mobile/src/components/title.tsx b/packages/editor-mobile/src/components/title.tsx
index b94f23acc..f1b9acd6e 100644
--- a/packages/editor-mobile/src/components/title.tsx
+++ b/packages/editor-mobile/src/components/title.tsx
@@ -17,20 +17,22 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-import React from "react";
-import { RefObject, useEffect, useRef } from "react";
+import { getFontById } from "@notesnook/editor";
+import React, { RefObject, useEffect, useRef } from "react";
import { EditorController } from "../hooks/useEditorController";
import styles from "./styles.module.css";
function Title({
controller,
title,
titlePlaceholder,
- readonly
+ readonly,
+ fontFamily
}: {
controller: RefObject;
title: string;
titlePlaceholder: string;
readonly: boolean;
+ fontFamily: string;
}) {
const titleRef = useRef(null);
const emitUpdate = useRef(true);
@@ -59,7 +61,7 @@ function Title({
paddingRight: 12,
paddingLeft: 12,
fontWeight: 600,
- fontFamily: "Open Sans",
+ fontFamily: getFontById(fontFamily)?.font || "Open Sans",
backgroundColor: "transparent",
color: "var(--nn_heading)",
caretColor: "var(--nn_accent)",
@@ -76,8 +78,13 @@ function Title({
}
export default React.memo(Title, (prev, next) => {
- if (prev.title !== next.title) return false;
- if (prev.titlePlaceholder !== next.titlePlaceholder) return false;
- if (prev.readonly !== next.readonly) return false;
+ if (
+ prev.title !== next.title ||
+ prev.titlePlaceholder !== next.titlePlaceholder ||
+ prev.readonly !== next.readonly ||
+ prev.fontFamily !== next.fontFamily
+ )
+ return false;
+
return true;
});
diff --git a/packages/editor-mobile/src/hooks/useSettings.ts b/packages/editor-mobile/src/hooks/useSettings.ts
index b38cf4a7b..0731d12a6 100644
--- a/packages/editor-mobile/src/hooks/useSettings.ts
+++ b/packages/editor-mobile/src/hooks/useSettings.ts
@@ -30,7 +30,9 @@ const initialState = {
noToolbar: globalThis.noToolbar,
noHeader: globalThis.noHeader,
readonly: globalThis.readonly,
- doubleSpacedLines: true
+ doubleSpacedLines: true,
+ fontFamily: "sans-serif",
+ fontSize: "16px"
};
global.settingsController = {
diff --git a/packages/editor-mobile/src/utils/index.ts b/packages/editor-mobile/src/utils/index.ts
index fcf5bbfea..974938b45 100644
--- a/packages/editor-mobile/src/utils/index.ts
+++ b/packages/editor-mobile/src/utils/index.ts
@@ -39,6 +39,8 @@ export type Settings = {
noHeader?: boolean;
doubleSpacedLines?: boolean;
corsProxy: string;
+ fontSize: number;
+ fontFamily: string;
};
/* eslint-disable no-var */
diff --git a/packages/editor/src/extensions/font-family/font-family.ts b/packages/editor/src/extensions/font-family/font-family.ts
index 3e268fd0f..e45aca98e 100644
--- a/packages/editor/src/extensions/font-family/font-family.ts
+++ b/packages/editor/src/extensions/font-family/font-family.ts
@@ -17,8 +17,8 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
import "@tiptap/extension-text-style";
-import { getFontConfig } from "@notesnook/theme/dist/theme/font";
import { Extension } from "@tiptap/core";
+import { getFontById } from "../../utils/font";
export type FontFamilyOptions = {
types: string[];
@@ -39,12 +39,6 @@ declare module "@tiptap/core" {
}
}
-const FONTS: Record = {
- monospace: getFontConfig().fonts.monospace,
- "sans-serif": getFontConfig().fonts.body,
- serif: `Noto Serif, Times New Roman, serif`
-};
-
export const FontFamily = Extension.create({
name: "fontFamily",
@@ -70,7 +64,8 @@ export const FontFamily = Extension.create({
const realFontFamily =
attributes["data-font-family"] || attributes.fontFamily;
- const font = FONTS[realFontFamily] || attributes.fontFamily;
+ const font =
+ getFontById(realFontFamily)?.font || attributes.fontFamily;
return {
"data-font-family": realFontFamily,
style: `font-family: ${font}`
diff --git a/packages/editor/src/extensions/font-size/font-size.ts b/packages/editor/src/extensions/font-size/font-size.ts
index 7e3fb0cc1..e0c3a4110 100644
--- a/packages/editor/src/extensions/font-size/font-size.ts
+++ b/packages/editor/src/extensions/font-size/font-size.ts
@@ -22,7 +22,6 @@ import "@tiptap/extension-text-style";
type FontSizeOptions = {
types: string[];
- defaultFontSize: number;
};
declare module "@tiptap/core" {
@@ -44,8 +43,7 @@ export const FontSize = Extension.create({
name: "fontSize",
defaultOptions: {
- types: ["textStyle"],
- defaultFontSize: 16
+ types: ["textStyle"]
},
addGlobalAttributes() {
@@ -54,7 +52,6 @@ export const FontSize = Extension.create({
types: this.options.types,
attributes: {
fontSize: {
- default: `${this.options.defaultFontSize}px`,
parseHTML: (element) => element.style.fontSize,
renderHTML: (attributes) => {
if (!attributes.fontSize) {
diff --git a/packages/editor/src/index.ts b/packages/editor/src/index.ts
index 0d5aaade7..588b936ff 100644
--- a/packages/editor/src/index.ts
+++ b/packages/editor/src/index.ts
@@ -273,6 +273,7 @@ export * from "./extensions/react";
export * from "./toolbar";
export * from "./types";
export * from "./utils/word-counter";
+export * from "./utils/font";
export {
useTiptap,
Toolbar,
diff --git a/packages/editor/src/toolbar/stores/toolbar-store.ts b/packages/editor/src/toolbar/stores/toolbar-store.ts
index 7deab1991..b05bbe1cf 100644
--- a/packages/editor/src/toolbar/stores/toolbar-store.ts
+++ b/packages/editor/src/toolbar/stores/toolbar-store.ts
@@ -39,6 +39,10 @@ interface ToolbarState {
closePopup: (popupId: string) => void;
closePopupGroup: (groupId: string, excluded: string[]) => void;
closeAllPopups: () => void;
+ fontFamily: string;
+ setFontFamily: (fontFamily: string) => void;
+ fontSize: number;
+ setFontSize: (fontSize: number) => void;
}
export const useToolbarStore = create((set, get) => ({
@@ -92,6 +96,16 @@ export const useToolbarStore = create((set, get) => ({
for (const key in state.openedPopups) {
state.openedPopups[key] = undefined;
}
+ }),
+ fontFamily: "sans-serif",
+ setFontFamily: (fontFamily) =>
+ set((state) => {
+ state.fontFamily = fontFamily;
+ }),
+ fontSize: 16,
+ setFontSize: (fontSize) =>
+ set((state) => {
+ state.fontSize = fontSize;
})
}));
diff --git a/packages/editor/src/toolbar/toolbar.tsx b/packages/editor/src/toolbar/toolbar.tsx
index ccd9d1ecc..cfb654ef2 100644
--- a/packages/editor/src/toolbar/toolbar.tsx
+++ b/packages/editor/src/toolbar/toolbar.tsx
@@ -41,6 +41,8 @@ type ToolbarProps = FlexProps & {
editor: Editor | null;
location: ToolbarLocation;
tools?: ToolbarDefinition;
+ defaultFontFamily: string;
+ defaultFontSize: number;
};
export function Toolbar(props: ToolbarProps) {
@@ -49,10 +51,11 @@ export function Toolbar(props: ToolbarProps) {
theme,
location,
tools = getDefaultPresets().default,
+ defaultFontFamily,
+ defaultFontSize,
sx,
...flexProps
} = props;
-
const toolbarTools = useMemo(
() => [...STATIC_TOOLBAR_GROUPS, ...tools],
[tools]
@@ -63,11 +66,23 @@ export function Toolbar(props: ToolbarProps) {
const setToolbarLocation = useToolbarStore(
(store) => store.setToolbarLocation
);
+ const setDefaultFontFamily = useToolbarStore((store) => store.setFontFamily);
+ const setDefaultFontSize = useToolbarStore((store) => store.setFontSize);
useEffect(() => {
setToolbarLocation(location);
}, [location, setToolbarLocation]);
+ useEffect(() => {
+ setDefaultFontFamily(defaultFontFamily);
+ setDefaultFontSize(defaultFontSize);
+ }, [
+ defaultFontFamily,
+ defaultFontSize,
+ setDefaultFontFamily,
+ setDefaultFontSize
+ ]);
+
if (!editor) return null;
return (
diff --git a/packages/editor/src/toolbar/tools/font.tsx b/packages/editor/src/toolbar/tools/font.tsx
index 538e6c351..72f6fa8f2 100644
--- a/packages/editor/src/toolbar/tools/font.tsx
+++ b/packages/editor/src/toolbar/tools/font.tsx
@@ -24,12 +24,16 @@ import { MenuItem } from "../../components/menu/types";
import { useCallback, useMemo } from "react";
import { Counter } from "../components/counter";
import { useRefValue } from "../../hooks/use-ref-value";
+import { useToolbarStore } from "../stores/toolbar-store";
+import { getFontById, getFontIds, getFonts } from "../../utils/font";
export function FontSize(props: ToolProps) {
const { editor } = props;
- const { fontSize: _fontSize } = editor.getAttributes("textStyle");
- const fontSize = _fontSize || "16px";
- const fontSizeAsNumber = useRefValue(parseInt(fontSize.replace("px", "")));
+ const defaultFontSize = useToolbarStore((store) => store.fontSize);
+ const { fontSize } = editor.getAttributes("textStyle");
+ const fontSizeAsNumber = useRefValue(
+ fontSize ? parseInt(fontSize.replace("px", "")) : defaultFontSize
+ );
const decreaseFontSize = useCallback(() => {
return Math.max(8, fontSizeAsNumber.current - 1);
@@ -56,27 +60,26 @@ export function FontSize(props: ToolProps) {
.setFontSize(`${increaseFontSize()}px`)
.run();
}}
- onReset={() => editor.current?.chain().focus().setFontSize("16px").run()}
+ onReset={() =>
+ editor.current
+ ?.chain()
+ .focus()
+ .setFontSize(`${defaultFontSize}px`)
+ .run()
+ }
value={fontSize}
/>
);
}
-const fontFamilies = {
- "Sans-serif": "Open Sans",
- Serif: "serif",
- Monospace: "monospace"
-};
export function FontFamily(props: ToolProps) {
const { editor } = props;
-
+ const defaultFontFamily = useToolbarStore((store) => store.fontFamily);
const currentFontFamily =
- Object.entries(fontFamilies)
- .find(([_key, value]) =>
- editor.isActive("textStyle", { fontFamily: value })
- )
- ?.map((a) => a)
- ?.at(0) || "Sans-serif";
+ getFontIds().find((id) =>
+ editor.isActive("textStyle", { fontFamily: id })
+ ) || defaultFontFamily;
+
const items = useMemo(
() => toMenuItems(editor, currentFontFamily),
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -87,7 +90,7 @@ export function FontFamily(props: ToolProps) {
@@ -96,16 +99,16 @@ export function FontFamily(props: ToolProps) {
function toMenuItems(editor: Editor, currentFontFamily: string): MenuItem[] {
const menuItems: MenuItem[] = [];
- for (const key in fontFamilies) {
- const value = fontFamilies[key as keyof typeof fontFamilies];
+ for (const font of getFonts()) {
menuItems.push({
- key,
+ key: font.id,
type: "button",
- title: key,
- isChecked: key === currentFontFamily,
- onClick: () => editor.current?.chain().focus().setFontFamily(value).run(),
+ title: font.title,
+ isChecked: font.id === currentFontFamily,
+ onClick: () =>
+ editor.current?.chain().focus().setFontFamily(font.id).run(),
styles: {
- fontFamily: value
+ fontFamily: font.font
}
});
}
diff --git a/packages/editor/src/utils/font.ts b/packages/editor/src/utils/font.ts
new file mode 100644
index 000000000..e6d0b9a0d
--- /dev/null
+++ b/packages/editor/src/utils/font.ts
@@ -0,0 +1,50 @@
+/*
+This file is part of the Notesnook project (https://notesnook.com/)
+
+Copyright (C) 2023 Streetwriters (Private) Limited
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+import { getFontConfig } from "@notesnook/theme/dist/theme/font";
+
+const FONTS = [
+ {
+ title: "Monospace",
+ id: "monospace",
+ font: getFontConfig().fonts.monospace
+ },
+ {
+ title: "Sans-serif",
+ id: "sans-serif",
+ font: getFontConfig().fonts.body
+ },
+ {
+ title: "Serif",
+ id: "serif",
+ font: `Noto Serif, Times New Roman, serif`
+ }
+];
+
+export function getFonts() {
+ return FONTS;
+}
+
+export function getFontById(id: string) {
+ return FONTS.find((a) => a.id === id);
+}
+
+export function getFontIds() {
+ return FONTS.map((a) => a.id);
+}
diff --git a/packages/editor/styles/styles.css b/packages/editor/styles/styles.css
index 2db06de0e..c69292a2f 100644
--- a/packages/editor/styles/styles.css
+++ b/packages/editor/styles/styles.css
@@ -1,7 +1,13 @@
+
+
.ProseMirror span * {
font-family: inherit;
}
+.ProseMirror {
+ font-family: inherit;
+}
+
.ProseMirror p.is-editor-empty:first-child::before {
color: var(--placeholder);
content: attr(data-placeholder);
@@ -87,6 +93,7 @@
.ProseMirror p {
margin-bottom: 0px;
+ font-family: inherit;
}
.ProseMirror > p[data-spacing="double"] {