Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
"@emoji-mart/data": "^1.2.1",
"@handlewithcare/prosemirror-inputrules": "^0.1.4",
"@shikijs/types": "^4",
"@tanstack/store": "^0.7.7",
"@tanstack/store": "^0.10.0",
"@tiptap/core": "^3.13.0",
"@tiptap/extension-bold": "^3.13.0",
"@tiptap/extension-code": "^3.13.0",
Expand Down
34 changes: 16 additions & 18 deletions packages/core/src/comments/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,24 +91,11 @@ export const CommentsExtension = createExtension(
const markType = CommentMark.name;

const userStore = new UserStore<User>(resolveUsers);
const store = createStore(
{
pendingComment: false,
selectedThreadId: undefined as string | undefined,
threadPositions: new Map<string, { from: number; to: number }>(),
},
{
onUpdate() {
// If the selected thread id changed, we need to update the decorations
if (
store.state.selectedThreadId !== store.prevState.selectedThreadId
) {
// So, we issue a transaction to update the decorations
editor.transact((tr) => tr.setMeta(PLUGIN_KEY, true));
}
},
},
);
const store = createStore({
pendingComment: false,
selectedThreadId: undefined as string | undefined,
threadPositions: new Map<string, { from: number; to: number }>(),
});

const updateMarksFromThreads = (threads: Map<string, ThreadData>) => {
editor.transact((tr) => {
Expand Down Expand Up @@ -261,6 +248,16 @@ export const CommentsExtension = createExtension(
const unsubscribe = threadStore.subscribe(updateMarksFromThreads);
updateMarksFromThreads(threadStore.getThreads());

let prevSelectedThreadId = store.state.selectedThreadId;
const storeSubscription = store.subscribe(() => {
// If the selected thread id changed, we need to update the decorations
if (store.state.selectedThreadId !== prevSelectedThreadId) {
prevSelectedThreadId = store.state.selectedThreadId;
// So, we issue a transaction to update the decorations
editor.transact((tr) => tr.setMeta(PLUGIN_KEY, true));
}
});

const unsubscribeOnSelectionChange = editor.onSelectionChange(() => {
if (store.state.pendingComment) {
store.setState((prev) => ({
Expand All @@ -272,6 +269,7 @@ export const CommentsExtension = createExtension(

return () => {
unsubscribe();
storeSubscription.unsubscribe();
unsubscribeOnSelectionChange();
};
},
Expand Down
9 changes: 3 additions & 6 deletions packages/core/src/editor/BlockNoteExtension.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Store, StoreOptions } from "@tanstack/store";
import { Store } from "@tanstack/store";
import { type AnyExtension } from "@tiptap/core";
import type { Plugin as ProsemirrorPlugin } from "prosemirror-state";
import type { PartialBlockNoDefaults } from "../schema/index.js";
Expand Down Expand Up @@ -233,9 +233,6 @@ export function createExtension<
} as any;
}

export function createStore<T = any>(
initialState: T,
options?: StoreOptions<T>,
): Store<T> {
return new Store(initialState, options);
export function createStore<T = any>(initialState: T): Store<T> {
return new Store(initialState);
}
4 changes: 2 additions & 2 deletions packages/core/src/extensions/Collaboration/ForkYDoc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export const ForkYDocExtension = createExtension(
]);

// Tell the store that the editor is now forked
store.setState({ isForked: true });
store.setState(() => ({ isForked: true }));
},

/**
Expand Down Expand Up @@ -146,7 +146,7 @@ export const ForkYDocExtension = createExtension(
// Reset the forked state
forkedState = undefined;
// Tell the store that the editor is no longer forked
store.setState({ isForked: false });
store.setState(() => ({ isForked: false }));
},
} as const;
},
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/extensions/FilePanel/FilePanel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const FilePanelExtension = createExtension(({ editor }) => {
const store = createStore<string | undefined>(undefined);

function closeMenu() {
store.setState(undefined);
store.setState(() => undefined);
}

return {
Expand Down Expand Up @@ -35,7 +35,7 @@ export const FilePanelExtension = createExtension(({ editor }) => {
},
closeMenu,
showMenu(blockId: string) {
store.setState(blockId);
store.setState(() => blockId);
},
} as const;
});
Original file line number Diff line number Diff line change
Expand Up @@ -67,22 +67,22 @@ export const FormattingToolbarExtension = createExtension(({ editor }) => {
return;
}
// re-evaluate whether the toolbar should be shown
store.setState(shouldShow());
store.setState(() => shouldShow());
});
const unsubscribeOnSelectionChange = editor.onSelectionChange(() => {
if (preventShowWhileMouseDown) {
return;
}
// re-evaluate whether the toolbar should be shown
store.setState(shouldShow());
store.setState(() => shouldShow());
});

// To mimic Notion's behavior, we listen to the mouse down event to set the `preventShowWhileMouseDown` flag
dom.addEventListener(
"pointerdown",
() => {
preventShowWhileMouseDown = true;
store.setState(false);
store.setState(() => false);
},
{ signal },
);
Expand All @@ -93,7 +93,7 @@ export const FormattingToolbarExtension = createExtension(({ editor }) => {
preventShowWhileMouseDown = false;
// We only want to re-show the toolbar if the mouse made the selection
if (editor.isFocused()) {
store.setState(shouldShow());
store.setState(() => shouldShow());
}
},
{ signal, capture: true },
Expand Down
25 changes: 13 additions & 12 deletions packages/core/src/extensions/ShowSelection/ShowSelection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,7 @@ const PLUGIN_KEY = new PluginKey(`blocknote-show-selection`);
* text editor is not focused.
*/
export const ShowSelectionExtension = createExtension(({ editor }) => {
const store = createStore(
{ enabledSet: new Set<string>() },
{
onUpdate() {
editor.transact((tr) => tr.setMeta(PLUGIN_KEY, {}));
},
},
);
const store = createStore({ enabledSet: new Set<string>() });
return {
key: "showSelection",
store,
Expand All @@ -41,6 +34,14 @@ export const ShowSelectionExtension = createExtension(({ editor }) => {
},
}),
],
mount() {
const subscription = store.subscribe(() => {
editor.transact((tr) => tr.setMeta(PLUGIN_KEY, {}));
});
return () => {
subscription.unsubscribe();
};
},
/**
* Show or hide the selection decoration
*
Expand All @@ -51,11 +52,11 @@ export const ShowSelectionExtension = createExtension(({ editor }) => {
* (e.g.: CreateLinkButton and AIExtension)
*/
showSelection(shouldShow: boolean, key: string) {
store.setState({
store.setState((prev) => ({
enabledSet: shouldShow
? new Set([...store.state.enabledSet, key])
: new Set([...store.state.enabledSet].filter((k) => k !== key)),
});
? new Set([...prev.enabledSet, key])
: new Set([...prev.enabledSet].filter((k) => k !== key)),
}));
},
} as const;
});
5 changes: 2 additions & 3 deletions packages/core/src/extensions/SideMenu/SideMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,7 @@ export class SideMenuView<
BSchema extends BlockSchema,
I extends InlineContentSchema,
S extends StyleSchema,
> implements PluginView
{
> implements PluginView {
public state?: SideMenuState<BSchema, I, S>;
public readonly emitUpdate: (state: SideMenuState<BSchema, I, S>) => void;

Expand Down Expand Up @@ -725,7 +724,7 @@ export const SideMenuExtension = createExtension(({ editor }) => {
view = new SideMenuView(editor, editorView, (state) => {
// TODO: Without spreading the state, in some cases like toggling
// `show`, this doesn't trigger an update.
store.setState({ ...state });
store.setState(() => ({ ...state }));
});
return view;
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ export const SuggestionMenu = createExtension(({ editor }) => {
view = new SuggestionMenuView(
editor,
(triggerCharacter, state) => {
store.setState({ ...state, triggerCharacter });
store.setState(() => ({ ...state, triggerCharacter }));
},
v,
);
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/extensions/TableHandles/TableHandles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,7 @@ export const TableHandlesExtension = createExtension(({ editor }) => {
key: tableHandlesPluginKey,
view: (editorView) => {
view = new TableHandlesView(editor as any, editorView, (state) => {
store.setState(
store.setState(() =>
state.block
? {
...state,
Expand Down
2 changes: 1 addition & 1 deletion packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
"@emoji-mart/data": "^1.2.1",
"@floating-ui/react": "^0.27.18",
"@floating-ui/utils": "^0.2.10",
"@tanstack/react-store": "0.7.7",
"@tanstack/react-store": "0.10.0",
"@tiptap/core": "^3.13.0",
"@tiptap/pm": "^3.13.0",
"@tiptap/react": "^3.13.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const AddCommentButtonInner = () => {

const onClick = useCallback(() => {
comments.startPendingComment();
store.setState(false);
store.setState(() => false);
}, [comments, store]);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,9 @@ export const CreateLinkButton = () => {
text={state.text}
range={state.range}
showTextField={false}
setToolbarOpen={(open) => formattingToolbar.store.setState(open)}
setToolbarOpen={(open) =>
formattingToolbar.store.setState(() => open)
}
/>
</Components.Generic.Popover.Content>
</Components.Generic.Popover.Root>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export const FormattingToolbarController = (props: {
// Needed as hooks like `useDismiss` call `onOpenChange` to change the
// open state.
onOpenChange: (open, _event, reason) => {
formattingToolbar.store.setState(open);
formattingToolbar.store.setState(() => open);

if (reason === "escape-key") {
editor.focus();
Expand Down
5 changes: 4 additions & 1 deletion packages/react/src/hooks/useExtension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,8 @@ export function useExtensionState<
if (!store) {
throw new Error("Store not found on plugin", { cause: { plugin } });
}
return useStore<ExtractStore<TStore>, TSelected>(store, ctx?.selector as any);
return useStore(
store as any,
(ctx?.selector ?? ((s: any) => s)) as any,
) as TSelected;
}
20 changes: 10 additions & 10 deletions packages/xl-ai/src/AIExtension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,12 +147,12 @@ export const AIExtension = createExtension(
.getExtension(ShowSelectionExtension)
?.showSelection(true, "aiMenu");
editor.isEditable = false;
store.setState({
store.setState(() => ({
aiMenuState: {
blockId: blockID,
status: "user-input",
},
});
}));

// Scrolls to the block when the menu opens.
const blockElement = editor.domElement?.querySelector(
Expand All @@ -165,9 +165,9 @@ export const AIExtension = createExtension(
* Close the AI menu
*/
closeAIMenu() {
store.setState({
store.setState(() => ({
aiMenuState: "closed",
});
}));
chatSession = undefined;
editor
.getExtension(ShowSelectionExtension)
Expand Down Expand Up @@ -350,20 +350,20 @@ export const AIExtension = createExtension(
if (status.status !== "error") {
throw new UnreachableCaseError(status.status);
}
this.store.setState({
this.store.setState(() => ({
aiMenuState: {
status: status.status,
error: status.error,
blockId: aiMenuState.blockId,
},
});
}));
} else {
this.store.setState({
this.store.setState(() => ({
aiMenuState: {
status: status,
blockId: aiMenuState.blockId,
},
});
}));
}
},

Expand Down Expand Up @@ -441,12 +441,12 @@ export const AIExtension = createExtension(
}

// NOTE: does this setState with an anon object trigger unnecessary re-renders?
store.setState({
store.setState(() => ({
aiMenuState: {
blockId,
status: "ai-writing",
},
});
}));

if (autoScroll) {
const blockElement = editor.prosemirrorView.domAtPos(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const AIToolbarButton = () => {
const position = selection.blocks[selection.blocks.length - 1].id;

ai.openAIMenuAtBlock(position);
formattingToolbar.store.setState(false);
formattingToolbar.store.setState(() => false);
};

if (!editor.isEditable) {
Expand Down
Loading
Loading