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 @codexteam/ui/dev/pages/components/Editor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
placeholder="Write something or press / to select a tool"
first-block-placeholder="Untitled"
autofocus
:inlineToolbar="true"
:inline-toolbar="true"
/>
</template>

Expand Down
2 changes: 1 addition & 1 deletion @codexteam/ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@codexteam/ui",
"version": "0.2.3",
"version": "0.2.5",
"type": "module",
"sideEffects": [
"*.css",
Expand Down
3 changes: 1 addition & 2 deletions @codexteam/ui/src/vue/components/editor/useEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,7 @@ export function useEditor(editorConfig: MaybeRefOrGetter<EditorConfig>, options:
* Destroy editor instance after unmount
*/
onBeforeUnmount(() => {
editor?.destroy();
editor = undefined;
destroyEditor();
});

return {
Expand Down
44 changes: 34 additions & 10 deletions src/application/services/useNoteEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ export const useNoteEditor = function useNoteEditor(options: UseNoteEditorOption
*/
const toolsUserConfigLoaded = ref<boolean>(false);

/**
* Incremented on each new load request to discard stale async results.
* Prevents race conditions when rapid note switching causes multiple
* concurrent loadToolsScripts invocations.
*/
let currentLoadId = 0;

/**
* Combine note and user tools
* Undefined when user or note is not loaded
Expand All @@ -102,10 +109,12 @@ export const useNoteEditor = function useNoteEditor(options: UseNoteEditorOption
});

/**
* Downloads passed tools scripts and toggles-on the isEditorReady flag
* Downloads passed tools scripts and returns the loaded config object.
* Does not mutate shared state — the caller is responsible for applying the result
* @param toolsConfigs - tools to download
* @returns loaded tools config
*/
async function loadToolsScripts(toolsConfigs: EditorTool[]): Promise<void> {
async function loadToolsScripts(toolsConfigs: EditorTool[]): Promise<Record<string, { class: EditorjsConfigTool; inlineToolbar: boolean }>> {
const loadedTools = await editorToolsService.getToolsLoaded(toolsConfigs);

/**
Expand All @@ -114,7 +123,7 @@ export const useNoteEditor = function useNoteEditor(options: UseNoteEditorOption
*/
const loadedToolsWithoutParagraph = loadedTools.filter(tool => tool.tool.name !== 'paragraph');

toolsUserConfig = Object.fromEntries(
return Object.fromEntries(
loadedToolsWithoutParagraph
.map(toolClassAndInfo => [
toolClassAndInfo.tool.name,
Expand All @@ -124,12 +133,6 @@ export const useNoteEditor = function useNoteEditor(options: UseNoteEditorOption
},
])
);
toolsUserConfigLoaded.value = true;

/**
* Now all tools are loaded, we're ready to use the editor
*/
isEditorReady.value = true;
}

/**
Expand All @@ -144,7 +147,28 @@ export const useNoteEditor = function useNoteEditor(options: UseNoteEditorOption
return;
}

await loadToolsScripts(tools);
const loadId = ++currentLoadId;

isEditorReady.value = false;
toolsUserConfigLoaded.value = false;

const loadedConfig = await loadToolsScripts(tools);

/**
* If a newer load request has superseded this one — discard stale results
* to prevent overwriting state with tools from a previous note.
*/
if (loadId !== currentLoadId) {
return;
}

toolsUserConfig = loadedConfig;
toolsUserConfigLoaded.value = true;

/**
* Now all tools are loaded, we're ready to use the editor
*/
isEditorReady.value = true;
}, {
immediate: true, // load tools if they are passed to the composable immediately
});
Expand Down