Skip to content

Commit

Permalink
refactor(editor): move editor.js to service application
Browse files Browse the repository at this point in the history
  • Loading branch information
ksenia_subbotina committed Jan 10, 2024
1 parent b378d4f commit 2424aee
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 148 deletions.
149 changes: 149 additions & 0 deletions src/application/services/useEditor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import { onBeforeUnmount, onMounted, ref, watch } from 'vue';
import Editor, { type OutputData, type API } from '@editorjs/editorjs';
import { loadScript } from '@/utils';

Check failure on line 3 in src/application/services/useEditor.ts

View workflow job for this annotation

GitHub Actions / Build notes.web

Cannot find module '@/utils' or its corresponding type declarations.
import type EditorTool from '@/domain/entities/EditorTool';
// @ts-expect-error: we need to rewrite plugins to TS to get their types
import Header from '@editorjs/header';
import { useAppState } from './useAppState';

/**
* Interface describing hook props
*/
interface EditorProps {
/**
* Content displayed in Editor
*/
content?: OutputData;

/**
* Allow edit content in Editor
*/
isReadOnly?: boolean;

/**
* Handler for every content change in Editor
*/
onChange: (api: API) => void
}

/**
* Application service for working with Editor
*
*/
export function useEditor({ content, isReadOnly, onChange } : EditorProps): void {
/**
* Editor.js instance
*/
const editor = ref<Editor | undefined>(undefined);

/**
* User notes tools
*/
const { userEditorTools } = useAppState();

/**
* Function for loading and adding tools to Editor
*/
const mountEditorOnce = async (): Promise<void> => {
Promise.allSettled(userEditorTools.value.map((spec: EditorTool) => {
if (spec.source.cdn === undefined || spec.source.cdn === "") {
return undefined;
}

return loadScript(spec.source.cdn);
})).then(async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const loadedTools: {[key: string]: any } = userEditorTools.value.reduce(
(acc, spec: EditorTool) => {
// @ts-expect-error: we need to rewrite plugins to TS to get their types
const windowPlugin = window[spec.exportName];

return {
...acc,
[spec.title]: windowPlugin,
};
},
{}
);


const editorInstance = new Editor({
/**
* Block Tools
*/
tools: {
header: {
class: Header,
config: {
placeholder: 'Title...',
},
},
// image: Image,
// code: CodeTool,
// list: List,
// delimiter: Delimiter,
// table: Table,
// warning: Warning,
// checklist: Checklist,
// linkTool: LinkTool,
// raw: RawTool,
// embed: Embed,

// /**
// * Inline Tools
// */
// inlineCode: InlineCode,
// marker: Marker,
...loadedTools,
},
data: content,
onChange,
readOnly: isReadOnly,
});

await editorInstance.isReady;

editor.value = editorInstance;
})
.catch(console.error);
};

/**
* Update editor instance when user tools was changed
*/
watch(userEditorTools, mountEditorOnce);

/**
* Set editor instance after first mount
*/
onMounted(() => {
if (userEditorTools.value.length > 0) {
mountEditorOnce().catch(console.info);
}
});

/**
* Add content to editor
*/
// eslint-disable-next-line @typescript-eslint/no-shadow
watch(() => content, (content) => {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (content === undefined) {
editor.value?.clear();

return;
}

if (editor.value) {
editor.value.render(content).catch(console.error);
}
});

/**
* Destroy editor instance after unmount
*/
onBeforeUnmount(() => {
editor.value?.destroy();
editor.value = undefined;
});
}
154 changes: 6 additions & 148 deletions src/presentation/components/editor/Editor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,58 +3,9 @@
</template>

<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref, watch } from 'vue';
import Editor, { type OutputData, type API } from '@editorjs/editorjs';
// @ts-expect-error: we need to rewrite plugins to TS to get their types
import Header from '@editorjs/header';
// // @ts-expect-error: we need to rewrite plugins to TS to get their types
// import Image from '@editorjs/image';
// // @ts-expect-error: we need to rewrite plugins to TS to get their types
// import CodeTool from '@editorjs/code';
// // @ts-expect-error: we need to rewrite plugins to TS to get their types
// import List from '@editorjs/list';
// // @ts-expect-error: we need to rewrite plugins to TS to get their types
// import Delimiter from '@editorjs/delimiter';
// // @ts-expect-error: we need to rewrite plugins to TS to get their types
// import Table from '@editorjs/table';
// // @ts-expect-error: we need to rewrite plugins to TS to get their types
// import Warning from '@editorjs/warning';
// // @ts-expect-error: we need to rewrite plugins to TS to get their types
// import Checklist from '@editorjs/checklist';
// // @ts-expect-error: we need to rewrite plugins to TS to get their types
// import LinkTool from '@editorjs/link';
// // @ts-expect-error: we need to rewrite plugins to TS to get their types
// import RawTool from '@editorjs/raw';
// // @ts-expect-error: we need to rewrite plugins to TS to get their types
// import Embed from '@editorjs/embed';
// // @ts-expect-error: we need to rewrite plugins to TS to get their types
// import InlineCode from '@editorjs/inline-code';
// // @ts-expect-error: we need to rewrite plugins to TS to get their types
// import Marker from '@editorjs/marker';
import EditorTool from '@/domain/entities/EditorTool';
import { useAppState } from '@/application/services/useAppState';
const { userEditorTools } = useAppState();
/**
* Load one tool at a time
*
* @param src - source path to tool
*/
function loadScript(src: string) {
return new Promise(function (resolve, reject) {
const editorToolScript = document.createElement('script');
editorToolScript.src = src;
editorToolScript.onload = resolve;
editorToolScript.onerror = reject;
document.head.appendChild(editorToolScript);
});
}
import { ref } from 'vue';
import { type OutputData, type API } from '@editorjs/editorjs';
import { useEditor } from '@/application/services/useEditor';
/**
* Define the props for the component
Expand All @@ -68,10 +19,6 @@ const emit = defineEmits<{
'change': [data: OutputData],
}>();
/**
* Editor.js instance
*/
const editor = ref<Editor | undefined>(undefined);
/**
* Attribute containing is-empty state.
Expand Down Expand Up @@ -127,98 +74,9 @@ async function onChange(api: API): Promise<void> {
emit('change', data);
}
const isEditorMounted = ref(false);
const mountEditorOnce = async () => {
console.log('mount');
isEditorMounted.value = true;
Promise.allSettled(userEditorTools.value.map((spec: EditorTool) => {
if (!spec.source.cdn) {
return;
}
return loadScript(spec.source.cdn);
})).then(async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const loadedTools: {[key: string]: any } = userEditorTools.value.reduce(
(acc, spec: EditorTool) => {
// @ts-expect-error: we need to rewrite plugins to TS to get their types
const windowPlugin = window[spec.exportName];
return {
...acc,
[spec.title]: windowPlugin,
};
},
{}
);
const editorInstance = new Editor({
/**
* Block Tools
*/
tools: {
header: {
class: Header,
config: {
placeholder: 'Title...',
},
},
// image: Image,
// code: CodeTool,
// list: List,
// delimiter: Delimiter,
// table: Table,
// warning: Warning,
// checklist: Checklist,
// linkTool: LinkTool,
// raw: RawTool,
// embed: Embed,
// /**
// * Inline Tools
// */
// inlineCode: InlineCode,
// marker: Marker,
...loadedTools,
},
data: props.content,
onChange,
readOnly: props.readOnly,
});
await editorInstance.isReady;
editor.value = editorInstance;
});
};
watch(userEditorTools, mountEditorOnce);
onMounted(() => {
console.log('mount', userEditorTools.value);
if (userEditorTools.value.length > 0) {
mountEditorOnce();
}
});
watch(() => props.content, (content) => {
if (content === undefined) {
editor.value?.clear();
return;
}
if (editor.value) {
editor.value.render(content);
}
});
onBeforeUnmount(() => {
editor.value?.destroy();
editor.value = undefined;
});
useEditor({ content: props.content,
isReadOnly: props.readOnly,
onChange });
defineExpose({
/**
Expand Down
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { loadScript } from './loadScript';
15 changes: 15 additions & 0 deletions src/utils/loadScript.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* Load one tool at a time
*
* @param src - source path to tool
*/
export function loadScript(src: string): Promise<Event> {
return new Promise(function (resolve, reject) {
const script = document.createElement('script');

script.src = src;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@/presentation/*": ["presentation/*"],
"@/presentation": ["presentation"],
"@/domain/*": ["domain/*"],
"@/utils": ["utils/*"],
"@/domain": ["domain"],
"@/application/*": ["application/*"],
"@/application": ["application"],
Expand Down

0 comments on commit 2424aee

Please sign in to comment.