Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add useEditorSelector hook to subscribe to specific properties of editor #2816

Merged
merged 14 commits into from
Dec 22, 2023
4 changes: 4 additions & 0 deletions apps/www/src/registry/default/example/playground-demo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ import { FixedToolbar } from '@/registry/default/plate-ui/fixed-toolbar';
import { FloatingToolbar } from '@/registry/default/plate-ui/floating-toolbar';
import { MentionCombobox } from '@/registry/default/plate-ui/mention-combobox';

import { TestToolbar } from './test-toolbar';

export const usePlaygroundPlugins = ({
id,
components = createPlateUI(),
Expand Down Expand Up @@ -344,6 +346,8 @@ export default function PlaygroundDemo({ id }: { id?: ValueId }) {
)}
/>

<TestToolbar />

{enabled['floating-toolbar'] && (
<FloatingToolbar>
{enabled['floating-toolbar-buttons'] && (
Expand Down
28 changes: 28 additions & 0 deletions apps/www/src/registry/default/example/test-toolbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React, { useEffect, useRef } from 'react';
import { isSelectionExpanded, useEditorSelector } from '@udecode/plate-common';

const useRenderCount = () => {
const renderCount = useRef(0);
useEffect(() => {
renderCount.current += 1;
});
return renderCount.current;
};

export const TestToolbar = () => {
const isExpanded = useEditorSelector(isSelectionExpanded, []);
const renderCount = useRenderCount();

return (
<>
{JSON.stringify(
{
renderCount, // Increments only when isExpanded changes
isExpanded,
},
null,
2
)}
</>
);
};
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"clsx": "^1.2.1",
"is-hotkey": "^0.2.0",
"jotai": "^2.6.0",
"jotai-x": "^1.0.1",
"jotai-x": "0.0.0-20231221184746",
"lodash": "^4.17.21",
"nanoid": "^3.3.6",
"react-hotkeys-hook": "^4.4.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/hooks/usePlateEffects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const usePlateEffects = <
}: UsePlateEffectsProps<V, E>) => {
const editor = useEditorRef<V, E>(id);

const states = usePlateStates<V, E>(id);
const states = usePlateStates(id);
const [rawPlugins, setRawPlugins] = states.rawPlugins();
const [, setPlugins] = states.plugins();

Expand Down
1 change: 0 additions & 1 deletion packages/core/src/libs/jotai.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export type { GetRecord, SetRecord, UseRecord } from 'jotai-x';
export { createAtomStore } from 'jotai-x';
39 changes: 15 additions & 24 deletions packages/core/src/stores/plate/createPlateStore.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import { Value } from '@udecode/slate';
import { atom } from 'jotai';

import {
createAtomStore,
GetRecord,
SetRecord,
UseRecord,
} from '../../libs/jotai';
import { createAtomStore } from '../../libs/jotai';
import { PlateEditor } from '../../types/PlateEditor';
import { PlateStoreState } from '../../types/PlateStore';

Expand Down Expand Up @@ -61,6 +57,16 @@ export const createPlateStore = <
} as PlateStoreState<V, E>,
{
name: 'plate',
extend: (atoms) => ({
trackedEditor: atom((get) => ({
editor: get(atoms.editor),
version: get(atoms.versionEditor),
})),
trackedSelection: atom((get) => ({
selection: get(atoms.editor).selection,
version: get(atoms.versionSelection),
})),
}),
}
);

Expand All @@ -70,24 +76,9 @@ export const {
PlateProvider: PlateStoreProvider,
} = createPlateStore();

export const usePlateSelectors = <
V extends Value = Value,
E extends PlateEditor<V> = PlateEditor<V>,
>(
id?: PlateId
): GetRecord<PlateStoreState<V, E>> => usePlateStore(id).get as any;
export const usePlateActions = <
V extends Value = Value,
E extends PlateEditor<V> = PlateEditor<V>,
>(
id?: PlateId
): SetRecord<PlateStoreState<V, E>> => usePlateStore(id).set as any;
export const usePlateStates = <
V extends Value = Value,
E extends PlateEditor<V> = PlateEditor<V>,
>(
id?: PlateId
): UseRecord<PlateStoreState<V, E>> => usePlateStore(id).use as any;
export const usePlateSelectors = (id?: PlateId) => usePlateStore(id).get;
export const usePlateActions = (id?: PlateId) => usePlateStore(id).set;
export const usePlateStates = (id?: PlateId) => usePlateStore(id).use;

/**
* Get the closest `Plate` id.
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/stores/plate/selectors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
export * from './useEditorReadOnly';
export * from './useEditorRef';
export * from './useEditorSelection';
export * from './useEditorSelector';
export * from './useEditorState';
export * from './useEditorVersion';
export * from './useSelectionVersion';
2 changes: 1 addition & 1 deletion packages/core/src/stores/plate/selectors/useEditorRef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ export const useEditorRef = <
E extends PlateEditor<V> = PlateEditor<V>,
>(
id?: PlateId
) => usePlateSelectors<V, E>(id).editor();
): E => usePlateSelectors(id).editor() as any;
11 changes: 3 additions & 8 deletions packages/core/src/stores/plate/selectors/useEditorSelection.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import { PlateId } from '../createPlateStore';
import { useEditorRef } from './useEditorRef';
import { useSelectionVersion } from './useSelectionVersion';
import { PlateId, usePlateSelectors } from '../createPlateStore';

/**
* Get the editor selection (deeply memoized).
*/
export const useEditorSelection = (id?: PlateId) => {
useSelectionVersion(id);

return useEditorRef(id).selection;
};
export const useEditorSelection = (id?: PlateId) =>
usePlateSelectors(id).trackedSelection().selection;
34 changes: 34 additions & 0 deletions packages/core/src/stores/plate/selectors/useEditorSelector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { DependencyList, useMemo } from 'react';
import { Value } from '@udecode/slate';
import { selectAtom } from 'jotai/utils';

import { PlateEditor } from '../../../types/PlateEditor';
import { PlateId, plateStore, usePlateSelectors } from '../createPlateStore';

export interface UseEditorSelectorOptions<T> {
id?: PlateId;
equalityFn?: (a: T, b: T) => boolean;
}

export const useEditorSelector = <
T,
V extends Value = Value,
E extends PlateEditor<V> = PlateEditor<V>,
>(
selector: (editor: E, prev?: T) => T,
deps: DependencyList,
{ id, equalityFn = (a: T, b: T) => a === b }: UseEditorSelectorOptions<T> = {}
): T => {
const selectorAtom = useMemo(
() =>
selectAtom<{ editor: E }, T>(
plateStore.atom.trackedEditor,
({ editor }, prev) => selector(editor, prev),
equalityFn
),
// eslint-disable-next-line react-hooks/exhaustive-deps
deps
);

return usePlateSelectors(id).atom(selectorAtom);
};
10 changes: 3 additions & 7 deletions packages/core/src/stores/plate/selectors/useEditorState.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { Value } from '@udecode/slate';

import { PlateEditor } from '../../../types/PlateEditor';
import { PlateId } from '../createPlateStore';
import { useEditorRef } from './useEditorRef';
import { useEditorVersion } from './useEditorVersion';
import { PlateId, usePlateSelectors } from '../createPlateStore';

/**
* Get editor state which is updated on editor change.
Expand All @@ -13,8 +11,6 @@ export const useEditorState = <
E extends PlateEditor<V> = PlateEditor<V>,
>(
id?: PlateId
) => {
useEditorVersion(id);

return useEditorRef<V, E>(id);
): E => {
return usePlateSelectors(id).trackedEditor().editor;
};
8 changes: 3 additions & 5 deletions packages/core/src/types/PlateEditorMethods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@ import { Value } from '@udecode/slate';

import { EXPOSED_STORE_KEYS, PlateStoreState } from './PlateStore';

import type { SetRecord } from '../libs/jotai';

export type PlateEditorMethods<V extends Value = Value> = {
reset: () => void;
redecorate: () => void;

// Example: editor.plate.set.readOnly(true)
plate: {
set: {
[K in (typeof EXPOSED_STORE_KEYS)[number]]: ReturnType<
SetRecord<PlateStoreState<V>>[K]
>;
[K in (typeof EXPOSED_STORE_KEYS)[number]]: (
value: PlateStoreState<V>[K]
) => void;
};
};
};
Loading
Loading