Skip to content

Commit

Permalink
feat: 🎸 optimize plugins to support state replacement
Browse files Browse the repository at this point in the history
  • Loading branch information
Saul-Mirone committed Apr 26, 2022
1 parent fe87f45 commit 1076e48
Show file tree
Hide file tree
Showing 16 changed files with 258 additions and 200 deletions.
3 changes: 2 additions & 1 deletion gh-pages/component/hooks/useDarkMode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
import { useEffect } from 'react';

const code = {
light: 'https://unpkg.com/prism-themes/themes/prism-material-light.css',
// light: 'https://unpkg.com/prism-themes/themes/prism-material-light.css',
light: 'https://unpkg.com/prism-themes/themes/prism-nord.css',
dark: 'https://unpkg.com/prism-themes/themes/prism-nord.css',
};

Expand Down
2 changes: 1 addition & 1 deletion gh-pages/pages/guide/getting-started/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Milkdown is a lightweight but powerful WYSIWYG markdown editor. It's made up by

With this pattern you can enable or disable any custom syntax and feature you like, such as table, latex and slash commands. You can even create your own plugin to support your awesome idea.

> :baby_bottle: Fun fact: The Milkdown documentation is rendered by milkdown.
> 🍼 Fun fact: The Milkdown documentation is rendered by milkdown.
---

Expand Down
36 changes: 24 additions & 12 deletions packages/core/src/internal-plugin/editor-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,27 +44,39 @@ export const editorView: MilkdownPlugin = (pre) => {
const root = ctx.get(rootCtx) || document.body;
const el = typeof root === 'string' ? document.querySelector(root) : root;

const container = el ? createViewContainer(el) : undefined;

ctx.update(prosePluginsCtx, (xs) =>
xs.concat(
new Plugin({
key,
view: () => ({
ctx.update(prosePluginsCtx, (xs) => [
new Plugin({
key,
view: (editorView) => {
const container = el ? createViewContainer(el) : undefined;

const handleDOM = () => {
if (container && el) {
const editor = editorView.dom;
el.replaceChild(container, editor);
container.appendChild(editor);
}
};
handleDOM();
return {
destroy: () => {
if (container?.parentNode) {
container?.parentNode.replaceChild(editorView.dom, container);
}
container?.remove();
},
}),
}),
),
);
};
},
}),
...xs,
]);

await ctx.waitTimers(editorViewTimerCtx);

const state = ctx.get(editorStateCtx);
const options = ctx.get(editorViewOptionsCtx);
const nodeViews = Object.fromEntries(ctx.get(viewCtx) as [string, ViewFactory][]);
const view = new EditorView(container, {
const view = new EditorView(el as Node, {
state,
nodeViews,
...options,
Expand Down
30 changes: 18 additions & 12 deletions packages/core/src/internal-plugin/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,14 @@ export const themeEnvironment: MilkdownPlugin = (pre) => {
xs.concat(
new Plugin({
key,
view: () => ({
destroy: () => {
emotion.flush();
},
}),
view: () => {
themeManager.runExecutor();
return {
destroy: () => {
emotion.flush();
},
};
},
}),
),
);
Expand All @@ -73,17 +76,20 @@ export const themeFactory = (createThemePack?: CreateThemePack): ThemePlugin =>
const emotion = ctx.get(emotionCtx);
const themeManager = ctx.get(themeManagerCtx);

createThemePack?.(emotion, themeManager);
overrideFn?.(emotion, themeManager);
themeManager.setExecutor(() => {
createThemePack?.(emotion, themeManager);
overrideFn?.(emotion, themeManager);

internalThemeKeys.forEach((key) => {
if (!themeManager.has(key as ThemeSliceKey)) {
console.warn('Theme key not found: ', key.sliceName);
}
internalThemeKeys.forEach((key) => {
if (!themeManager.has(key as ThemeSliceKey)) {
console.warn('Theme key not found: ', key.sliceName);
}
});

themeManager.get(ThemeGlobal, undefined);
});

ctx.done(ThemeReady);
themeManager.get(ThemeGlobal, undefined);
};
theme.override = (fn) => {
overrideFn = fn;
Expand Down
19 changes: 17 additions & 2 deletions packages/design-system/src/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ type GetKey<Key extends ThemeSliceKey> = Key extends ThemeSliceKey<any, any, inf
export class ThemeManager {
#container = createContainer();
#cache: Map<string, ThemeSlice> = new Map();
#flushListener: Array<() => void> = [];
#flushListener: Set<() => void> = new Set();
#executor: () => void = () => {
/* noop */
};

inject<Ret = unknown, T = undefined>(key: ThemeSliceKey<Ret, T>): void {
key(this.#container.sliceMap);
Expand Down Expand Up @@ -70,7 +73,9 @@ export class ThemeManager {
}

onFlush(fn: () => void, callWhenRegister = true): void {
this.#flushListener.push(fn);
if (!this.#flushListener.has(fn)) {
this.#flushListener.add(fn);
}
if (callWhenRegister) {
fn();
}
Expand All @@ -80,8 +85,18 @@ export class ThemeManager {
const emotion = ctx.get(emotionCtx);
emotion.flush();
await theme(ctx as unknown as Pre)(ctx);
this.runExecutor();
this.#flushListener.forEach((f) => f());
}

setExecutor(executor: () => void): void {
executor();
this.#executor = executor;
}

runExecutor(): void {
this.#executor();
}
}

export const themeManagerCtx = createSlice(new ThemeManager(), 'themeManager');
Expand Down
60 changes: 30 additions & 30 deletions packages/plugin-math/src/math-inline/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,40 +157,40 @@ export const mathInline = createNode<string, Options>((utils, options) => {
}),
],
prosePlugins: (type, ctx) => {
const inputChipRenderer = utils.themeManager.get<ThemeInputChipType>('input-chip', {
placeholder: 'Input Math',
onUpdate: (value) => {
ctx.get(commandsCtx).call(ModifyInlineMath, value);
},
isBindMode: true,
});
if (!inputChipRenderer) return [];
const shouldDisplay = (view: EditorView) => {
return Boolean(type && findSelectedNodeOfType(view.state.selection, type));
};
const getCurrentLink = (view: EditorView) => {
const result = findSelectedNodeOfType(view.state.selection, type);
if (!result) return;

const value = result.node.attrs['value'];
return value;
};
const renderByView = (view: EditorView) => {
if (!view.editable) {
return;
}
const display = shouldDisplay(view);
if (display) {
inputChipRenderer.show(view);
inputChipRenderer.update(getCurrentLink(view));
} else {
inputChipRenderer.hide();
}
};
return [
new Plugin({
key,
view: (editorView) => {
const inputChipRenderer = utils.themeManager.get<ThemeInputChipType>('input-chip', {
placeholder: 'Input Math',
onUpdate: (value) => {
ctx.get(commandsCtx).call(ModifyInlineMath, value);
},
isBindMode: true,
});
if (!inputChipRenderer) return {};
const shouldDisplay = (view: EditorView) => {
return Boolean(type && findSelectedNodeOfType(view.state.selection, type));
};
const getCurrentLink = (view: EditorView) => {
const result = findSelectedNodeOfType(view.state.selection, type);
if (!result) return;

const value = result.node.attrs['value'];
return value;
};
const renderByView = (view: EditorView) => {
if (!view.editable) {
return;
}
const display = shouldDisplay(view);
if (display) {
inputChipRenderer.show(view);
inputChipRenderer.update(getCurrentLink(view));
} else {
inputChipRenderer.hide();
}
};
inputChipRenderer.init(editorView);
renderByView(editorView);

Expand Down
6 changes: 4 additions & 2 deletions packages/plugin-menu/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ export const menuPlugin = createPlugin<string, Options>((utils, options) => {
key: menuKey,
view: (editorView) => {
initIfNecessary(ctx, editorView);
if (editorView.editable) {
manager?.update(editorView);
}
return {
update: (view) => {
initIfNecessary(ctx, editorView);
Expand All @@ -61,8 +64,7 @@ export const menuPlugin = createPlugin<string, Options>((utils, options) => {
}
},
destroy: () => {
menu?.remove();
menu = null;
restoreDOM?.();
},
};
},
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-tooltip/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const tooltipPlugin = create<string, TooltipOptions>((utils, options) =>
},
},
view: (editorView) => {
manager.render(editorView);
manager.recreate(editorView);
return {
update: manager.update,
destroy: manager.destroy,
Expand Down
7 changes: 6 additions & 1 deletion packages/plugin-tooltip/src/selection-marks-tooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { createButtonManager } from './button-manager';
import type { ButtonMap } from './item';

export const createPlugin = (buttonMap: ButtonMap, utils: Utils, bottom: boolean, containerClassName: string) => {
const buttonManager = createButtonManager(buttonMap, utils, bottom, containerClassName);
let buttonManager = createButtonManager(buttonMap, utils, bottom, containerClassName);
let shouldHide = false;

const hide = () => {
Expand All @@ -28,6 +28,11 @@ export const createPlugin = (buttonMap: ButtonMap, utils: Utils, bottom: boolean
};

return {
recreate: (editorView: EditorView) => {
buttonManager = createButtonManager(buttonMap, utils, bottom, containerClassName);
buttonManager.render(editorView);
update(editorView);
},
update,
destroy: () => {
buttonManager.destroy();
Expand Down
98 changes: 49 additions & 49 deletions packages/preset-commonmark/src/mark/link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,59 +118,59 @@ export const link = createMark<string, LinkOptions>((utils, options) => {
}),
],
prosePlugins: (type, ctx) => {
const inputChipRenderer = utils.themeManager.get<ThemeInputChipType>('input-chip', {
placeholder: options?.input?.placeholder ?? 'Input Web Link',
buttonText: options?.input?.buttonText,
onUpdate: (value) => {
ctx.get(commandsCtx).call(ModifyLink, value);
},
});
if (!inputChipRenderer) return [];
const shouldDisplay = (view: EditorView) => {
const { selection, doc } = view.state;
const { from, to } = selection;

return (
selection.empty &&
selection instanceof TextSelection &&
doc.rangeHasMark(from, from === to ? to + 1 : to, type)
);
};
const getCurrentLink = (view: EditorView) => {
const { selection } = view.state;
let node: ProseNode | undefined;
const { from, to } = selection;
view.state.doc.nodesBetween(from, from === to ? to + 1 : to, (n) => {
if (type.isInSet(n.marks)) {
node = n;
return false;
}
return;
});
if (!node) return;

const mark = node.marks.find((m) => m.type === type);
if (!mark) return;

const value = mark.attrs['href'];
return value;
};
const renderByView = (view: EditorView) => {
if (!view.editable) {
return;
}
const display = shouldDisplay(view);
if (display) {
inputChipRenderer.show(view);
inputChipRenderer.update(getCurrentLink(view));
} else {
inputChipRenderer.hide();
}
};
return [
new Plugin({
key,
view: (editorView) => {
const inputChipRenderer = utils.themeManager.get<ThemeInputChipType>('input-chip', {
placeholder: options?.input?.placeholder ?? 'Input Web Link',
buttonText: options?.input?.buttonText,
onUpdate: (value) => {
ctx.get(commandsCtx).call(ModifyLink, value);
},
});
if (!inputChipRenderer) return {};
const shouldDisplay = (view: EditorView) => {
const { selection, doc } = view.state;
const { from, to } = selection;

return (
selection.empty &&
selection instanceof TextSelection &&
doc.rangeHasMark(from, from === to ? to + 1 : to, type)
);
};
const getCurrentLink = (view: EditorView) => {
const { selection } = view.state;
let node: ProseNode | undefined;
const { from, to } = selection;
view.state.doc.nodesBetween(from, from === to ? to + 1 : to, (n) => {
if (type.isInSet(n.marks)) {
node = n;
return false;
}
return;
});
if (!node) return;

const mark = node.marks.find((m) => m.type === type);
if (!mark) return;

const value = mark.attrs['href'];
return value;
};
const renderByView = (view: EditorView) => {
if (!view.editable) {
return;
}
const display = shouldDisplay(view);
if (display) {
inputChipRenderer.show(view);
inputChipRenderer.update(getCurrentLink(view));
} else {
inputChipRenderer.hide();
}
};
inputChipRenderer.init(editorView);
renderByView(editorView);

Expand Down

1 comment on commit 1076e48

@vercel
Copy link

@vercel vercel bot commented on 1076e48 Apr 26, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.