Skip to content

Commit

Permalink
feat(bundle): group editor settings by scope (#433)
Browse files Browse the repository at this point in the history
  • Loading branch information
d3m1d0v authored Oct 21, 2024
1 parent 40d4a98 commit d4097c2
Show file tree
Hide file tree
Showing 16 changed files with 372 additions and 162 deletions.
15 changes: 8 additions & 7 deletions demo/Playground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,21 @@ import {Button, DropdownMenu} from '@gravity-ui/uikit';
import {toaster} from '@gravity-ui/uikit/toaster-singleton-react-18';

import {
MarkdownEditorMode,
type EscapeConfig,
type MarkdownEditorMode,
MarkdownEditorView,
MarkdownEditorViewProps,
MarkupString,
type MarkdownEditorViewProps,
type MarkupString,
NumberInput,
RenderPreview,
ToolbarGroupData,
UseMarkdownEditorProps,
type RenderPreview,
type ToolbarGroupData,
type UseMarkdownEditorProps,
logger,
markupToolbarConfigs,
useMarkdownEditor,
wysiwygToolbarConfigs,
} from '../src';
import type {EscapeConfig, ToolbarActionData} from '../src/bundle/Editor';
import type {ToolbarActionData} from '../src/bundle/Editor';
import {Extension} from '../src/cm/state';
import {FoldingHeading} from '../src/extensions/yfm/FoldingHeading';
import {Math} from '../src/extensions/yfm/Math';
Expand Down
8 changes: 4 additions & 4 deletions demo/RememberMode.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React, {useEffect, useState} from 'react';
// eslint-disable-next-line import/no-extraneous-dependencies
import type {Meta, StoryFn} from '@storybook/react';

import {EditorMode} from '../src/bundle/Editor';
import type {MarkdownEditorMode} from '../src/bundle';

import {Playground as PlaygroundComponent, PlaygroundProps} from './Playground';

Expand Down Expand Up @@ -167,10 +167,10 @@ const args: Partial<PlaygroundStoryProps> = {
};

export const RememberMode: StoryFn<PlaygroundStoryProps> = (props) => {
const [mode, setMode] = useState<EditorMode>();
const [mode, setMode] = useState<MarkdownEditorMode>();
const [splitModeEnabled, setSplitModeEnabled] = useState<boolean>();

const handleChangeEditorType = (mode: EditorMode) => {
const handleChangeEditorType = (mode: MarkdownEditorMode) => {
localStorage.setItem('markdownEditorMode', mode);
};

Expand All @@ -183,7 +183,7 @@ export const RememberMode: StoryFn<PlaygroundStoryProps> = (props) => {
const storedSplitModeEnabled = localStorage.getItem('markdownEditorSplitModeEnabled');

if (storedMode) {
setMode(storedMode as EditorMode);
setMode(storedMode as MarkdownEditorMode);
setSplitModeEnabled(storedSplitModeEnabled === 'true');
}
}, []);
Expand Down
138 changes: 47 additions & 91 deletions src/bundle/Editor.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,32 @@
import type {ReactNode} from 'react';

import type {Extension as CodemirrorExtension} from '@codemirror/state';
import {EditorView as CMEditorView} from '@codemirror/view';
import {TextSelection} from 'prosemirror-state';
import {EditorView as PMEditorView} from 'prosemirror-view';

import {CommonEditor, MarkupString} from '../common';
import {ActionStorage, WysiwygEditor, WysiwygEditorOptions} from '../core';
import {ReactRenderStorage, RenderStorage} from '../extensions';
import type {CommonEditor, MarkupString} from '../common';
import {
type ActionStorage,
type EscapeConfig,
WysiwygEditor,
type WysiwygEditorOptions,
} from '../core';
import {ReactRenderStorage, type RenderStorage} from '../extensions';
import {i18n} from '../i18n/bundle';
import {logger} from '../logger';
import {type CreateCodemirrorParams, createCodemirror} from '../markup/codemirror';
import type {YfmLangOptions} from '../markup/codemirror/yfm';
import {CodeEditor, Editor as MarkupEditor} from '../markup/editor';
import {Emitter, Receiver, SafeEventEmitter} from '../utils/event-emitter';
import {createCodemirror} from '../markup/codemirror';
import {type CodeEditor, Editor as MarkupEditor} from '../markup/editor';
import {type Emitter, type Receiver, SafeEventEmitter} from '../utils/event-emitter';
import type {FileUploadHandler} from '../utils/upload';

export type EditorMode = 'wysiwyg' | 'markup';
export type SplitMode = false | 'horizontal' | 'vertical';
export type EditorPreset = 'zero' | 'commonmark' | 'default' | 'yfm' | 'full';
export type RenderPreview = ({
getValue,
mode,
}: {
getValue: () => MarkupString;
mode: 'preview' | 'split';
}) => ReactNode;
import type {
MarkdownEditorMode as EditorMode,
MarkdownEditorPreset as EditorPreset,
MarkdownEditorOptions,
MarkdownEditorMarkupConfig as MarkupConfig,
RenderPreview,
MarkdownEditorSplitMode as SplitMode,
} from './types';

export type ToolbarActionData = {
editorMode: EditorMode;
Expand Down Expand Up @@ -105,72 +106,18 @@ export interface EditorInt

type SetEditorModeOptions = Pick<ChangeEditorModeOptions, 'emit'>;

type ChangeEditorModeOptions = {
export type ChangeEditorModeOptions = {
mode: EditorMode;
reason: 'error-boundary' | 'settings' | 'manually';
emit?: boolean;
};

export type MarkupConfig = {
/** Additional extensions for codemirror instance. */
extensions?: CreateCodemirrorParams['extensions'];
/** Can be used to disable some of the default extensions */
disabledExtensions?: CreateCodemirrorParams['disabledExtensions'];
/** Additional keymaps for codemirror instance */
keymaps?: CreateCodemirrorParams['keymaps'];
/** Overrides the default placeholder content. */
placeholder?: CreateCodemirrorParams['placeholder'];
/**
* Additional language data for markdown language in codemirror.
* Can be used to configure additional autocompletions and others.
* See more https://codemirror.net/docs/ref/#state.EditorState.languageDataAt
*/
languageData?: YfmLangOptions['languageData'];
/** Config for @codemirror/autocomplete https://codemirror.net/docs/ref/#autocomplete.autocompletion%5Econfig */
autocompletion?: CreateCodemirrorParams['autocompletion'];
};

export type EscapeConfig = {
commonEscape?: RegExp;
startOfLineEscape?: RegExp;
};

export type EditorOptions = Pick<
WysiwygEditorOptions,
'allowHTML' | 'linkify' | 'linkifyTlds' | 'extensions'
MarkdownEditorOptions,
'md' | 'initial' | 'handlers' | 'experimental' | 'markupConfig' | 'wysiwygConfig'
> & {
initialMarkup?: MarkupString;
/** @default 'wysiwyg' */
initialEditorMode?: EditorMode;
/** @default true */
initialToolbarVisible?: boolean;
/** @default false
* Has no effect if splitMode is false or undefined
*/
initialSplitModeEnabled?: boolean;
renderStorage: ReactRenderStorage;
fileUploadHandler?: FileUploadHandler;
/**
* If we need to set dimensions for uploaded images
*
* @default false
*/
needToSetDimensionsForUploadedImages?: boolean;
/**
* Called before switching from the markup editor to the wysiwyg editor.
* You can use it to pre-process the value from the markup editor before it gets into the wysiwyg editor.
*/
prepareRawMarkup?: (value: MarkupString) => MarkupString;
experimental_beforeEditorModeChange?: (
options: Pick<ChangeEditorModeOptions, 'mode' | 'reason'>,
) => boolean | undefined;
splitMode?: SplitMode;
renderPreview?: RenderPreview;
preset: EditorPreset;
/** @deprecated Put extra extensions via MarkdownEditorMarkupConfig */
extraMarkupExtensions?: CodemirrorExtension[];
markupConfig?: MarkupConfig;
escapeConfig?: EscapeConfig;
};

/** @internal */
Expand Down Expand Up @@ -337,30 +284,39 @@ export class EditorImpl extends SafeEventEmitter<EventMapInt> implements EditorI

constructor(opts: EditorOptions) {
super({onError: logger.error.bind(logger)});
this.#editorMode = opts.initialEditorMode ?? 'wysiwyg';
this.#toolbarVisible = Boolean(opts.initialToolbarVisible);
this.#splitMode = (opts.renderPreview && opts.splitMode) ?? false;
this.#splitModeEnabled = (this.#splitMode && opts.initialSplitModeEnabled) ?? false;
this.#renderPreview = opts.renderPreview;

this.#markup = opts.initialMarkup ?? '';
const {
md = {},
initial = {},
handlers = {},
experimental = {},
markupConfig = {},
wysiwygConfig = {},
} = opts;

this.#editorMode = initial.mode ?? 'wysiwyg';
this.#toolbarVisible = initial.toolbarVisible ?? true;
this.#splitMode = (markupConfig.renderPreview && markupConfig.splitMode) ?? false;
this.#splitModeEnabled = (this.#splitMode && initial.splitModeEnabled) ?? false;
this.#renderPreview = markupConfig.renderPreview;

this.#markup = initial.markup ?? '';

this.#preset = opts.preset ?? 'full';
this.#linkify = opts.linkify;
this.#linkifyTlds = opts.linkifyTlds;
this.#allowHTML = opts.allowHTML;
this.#extensions = opts.extensions;
this.#linkify = md.linkify;
this.#linkifyTlds = md.linkifyTlds;
this.#allowHTML = md.html;
this.#extensions = wysiwygConfig.extensions;
this.#markupConfig = {...opts.markupConfig};
this.#markupConfig.extensions ??= opts.extraMarkupExtensions;

this.#renderStorage = opts.renderStorage;
this.#fileUploadHandler = opts.fileUploadHandler;
this.#fileUploadHandler = handlers.uploadFile;
this.#needToSetDimensionsForUploadedImages = Boolean(
opts.needToSetDimensionsForUploadedImages,
experimental.needToSetDimensionsForUploadedImages,
);
this.#prepareRawMarkup = opts.prepareRawMarkup;
this.#escapeConfig = opts.escapeConfig;
this.#beforeEditorModeChange = opts.experimental_beforeEditorModeChange;
this.#prepareRawMarkup = experimental.prepareRawMarkup;
this.#escapeConfig = wysiwygConfig.escapeConfig;
this.#beforeEditorModeChange = experimental.beforeEditorModeChange;
}

// ---> implements CodeEditor
Expand Down
5 changes: 3 additions & 2 deletions src/bundle/MarkdownEditorView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {logger} from '../logger';
import {ToasterContext, useBooleanState, useSticky} from '../react-utils';
import {isMac} from '../utils';

import type {Editor, EditorInt, EditorMode} from './Editor';
import type {Editor, EditorInt} from './Editor';
import {HorizontalDrag} from './HorizontalDrag';
import {MarkupEditorView} from './MarkupEditorView';
import {SplitModeView} from './SplitModeView';
Expand All @@ -28,6 +28,7 @@ import {
import {useMarkdownEditorContext} from './context';
import {EditorSettings, EditorSettingsProps} from './settings';
import {stickyCn} from './sticky';
import type {MarkdownEditorMode} from './types';

import '../styles/styles.scss';
import './MarkdownEditorView.scss'; // eslint-disable-line import/order
Expand Down Expand Up @@ -91,7 +92,7 @@ export const MarkdownEditorView = React.forwardRef<HTMLDivElement, MarkdownEdito
}, [editor, rerender]);

const onModeChange = React.useCallback(
(type: EditorMode) => {
(type: MarkdownEditorMode) => {
editor.changeEditorMode({mode: type, reason: 'settings'});
unsetShowPreview();
},
Expand Down
5 changes: 3 additions & 2 deletions src/bundle/MarkupEditorView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import {ReactRendererComponent} from '../extensions';
import {logger} from '../logger';
import {useRenderTime} from '../react-utils/hooks';

import type {EditorInt, SplitMode} from './Editor';
import type {EditorInt} from './Editor';
import {MarkupEditorComponent} from './MarkupEditorComponent';
import {ToolbarView} from './ToolbarView';
import type {MToolbarData, MToolbarItemData} from './config/markup';
import {MarkupToolbarContextProvider} from './toolbar/markup/context';
import type {MarkdownEditorSplitMode} from './types';

import './MarkupEditorView.scss';

Expand All @@ -23,7 +24,7 @@ export type MarkupEditorViewProps = ClassNameProps & {
toolbarVisible?: boolean;
stickyToolbar?: boolean;
toolbarClassName?: string;
splitMode?: SplitMode;
splitMode?: MarkdownEditorSplitMode;
splitModeEnabled: boolean;
hiddenActionsConfig?: MToolbarItemData[];
children?: React.ReactNode;
Expand Down
5 changes: 3 additions & 2 deletions src/bundle/ToolbarView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import {i18n} from '../i18n/menubar';
import {useSticky} from '../react-utils/useSticky';
import {FlexToolbar, ToolbarData, ToolbarItemData} from '../toolbar';

import type {EditorInt, EditorMode} from './Editor';
import type {EditorInt} from './Editor';
import {stickyCn} from './sticky';
import type {MarkdownEditorMode} from './types';

export type ToolbarViewProps<T> = ClassNameProps & {
editor: EditorInt;
editorMode: EditorMode;
editorMode: MarkdownEditorMode;
toolbarEditor: T;
toolbarFocus: () => void;
toolbarConfig: ToolbarData<T>;
Expand Down
6 changes: 3 additions & 3 deletions src/bundle/config/markup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,10 @@ import {
ToolbarReactComponentData,
ToolbarSingleItemData,
} from '../../toolbar/types';
import type {EditorPreset} from '../Editor';
import {MToolbarColors} from '../toolbar/markup/MToolbarColors';
import {MToolbarFilePopup} from '../toolbar/markup/MToolbarFilePopup';
import {MToolbarImagePopup} from '../toolbar/markup/MToolbarImagePopup';
import type {MarkdownEditorPreset} from '../types';

import {ActionName} from './action-names';
import {icons} from './icons';
Expand Down Expand Up @@ -545,7 +545,7 @@ export const mTabsItemData: MToolbarSingleItemData = {

export const mHiddenData = [mHruleItemData, mTabsItemData];

export const mToolbarConfigByPreset: Record<EditorPreset, MToolbarData> = {
export const mToolbarConfigByPreset: Record<MarkdownEditorPreset, MToolbarData> = {
zero: [mHistoryGroupConfig],
commonmark: [
mHistoryGroupConfig,
Expand Down Expand Up @@ -592,7 +592,7 @@ export const mToolbarConfigByPreset: Record<EditorPreset, MToolbarData> = {
full: mToolbarConfig.slice(),
};

export const mHiddenDataByPreset: Record<EditorPreset, MToolbarItemData[]> = {
export const mHiddenDataByPreset: Record<MarkdownEditorPreset, MToolbarItemData[]> = {
zero: [],
commonmark: [
...mHeadingListConfig.data,
Expand Down
10 changes: 5 additions & 5 deletions src/bundle/config/wysiwyg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ import {
ToolbarListItemData,
ToolbarSingleItemData,
} from '../../toolbar/types';
import type {EditorPreset} from '../Editor';
import {WToolbarColors} from '../toolbar/wysiwyg/WToolbarColors';
import {WToolbarTextSelect} from '../toolbar/wysiwyg/WToolbarTextSelect';
import type {MarkdownEditorPreset} from '../types';

import {ActionName} from './action-names';
import {icons} from './icons';
Expand Down Expand Up @@ -581,7 +581,7 @@ export const wMermaidItemData: WToolbarSingleItemData = {
isEnable: (e) => e.actions.createMermaid.isEnable(),
};

export const wToolbarConfigByPreset: Record<EditorPreset, WToolbarData> = {
export const wToolbarConfigByPreset: Record<MarkdownEditorPreset, WToolbarData> = {
zero: [wHistoryGroupConfig],
commonmark: [
wHistoryGroupConfig,
Expand Down Expand Up @@ -652,7 +652,7 @@ export const wToolbarConfigByPreset: Record<EditorPreset, WToolbarData> = {
full: wToolbarConfig.slice(),
};

export const wCommandMenuConfigByPreset: Record<EditorPreset, WToolbarItemData[]> = {
export const wCommandMenuConfigByPreset: Record<MarkdownEditorPreset, WToolbarItemData[]> = {
zero: [],
commonmark: [
...wHeadingListConfig.data,
Expand Down Expand Up @@ -688,15 +688,15 @@ export const wCommandMenuConfigByPreset: Record<EditorPreset, WToolbarItemData[]
full: wCommandMenuConfig.slice(),
};

export const wHiddenDataByPreset: Record<EditorPreset, WToolbarItemData[]> = {
export const wHiddenDataByPreset: Record<MarkdownEditorPreset, WToolbarItemData[]> = {
zero: wCommandMenuConfigByPreset.zero.slice(),
commonmark: wCommandMenuConfigByPreset.commonmark.slice(),
default: wCommandMenuConfigByPreset.default.slice(),
yfm: wCommandMenuConfigByPreset.yfm.slice(),
full: wCommandMenuConfigByPreset.full.slice(),
};

export const wSelectionMenuConfigByPreset: Record<EditorPreset, SelectionContextConfig> = {
export const wSelectionMenuConfigByPreset: Record<MarkdownEditorPreset, SelectionContextConfig> = {
zero: [],
commonmark: [
[textContextItemData],
Expand Down
9 changes: 1 addition & 8 deletions src/bundle/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
export type {
Editor as MarkdownEditorInstance,
EditorMode as MarkdownEditorMode,
EditorPreset as MarkdownEditorPreset,
MarkupConfig as MarkdownEditorMarkupConfig,
RenderPreview,
SplitMode,
} from './Editor';
export * from './types';
export {MarkdownEditorProvider, useMarkdownEditorContext} from './context';
export {useMarkdownEditor} from './useMarkdownEditor';
export type {UseMarkdownEditorProps} from './useMarkdownEditor';
Expand Down
Loading

0 comments on commit d4097c2

Please sign in to comment.