Improve types
This commit is contained in:
		
							parent
							
								
									544a1614a0
								
							
						
					
					
						commit
						78dd4f5189
					
				| @ -69,10 +69,15 @@ class EditorJSWrapper { | ||||
|        | ||||
|       try { | ||||
|         // If the type exists in tools, add that block type, otherwise treat as paragraph
 | ||||
|         // TODO: Make more generic and allow for plugin to define this behavior
 | ||||
|         const blockType = this.tools[type] ? type : 'paragraph'; | ||||
|         const data = blockType === 'paragraph' ? { text: type } : undefined; | ||||
|         const data = blockType === 'paragraph' ?  | ||||
|           { text: type } :  | ||||
|           blockType === 'checklist' ?  | ||||
|             { items: [{ text: '', checked: false }] } :  | ||||
|             undefined; | ||||
|          | ||||
|         await this.editor.blocks.insert(blockType, data); | ||||
|         await this.editor.blocks.insert(blockType, { data }); | ||||
|         await this.syncState(); | ||||
|       } catch (err) { | ||||
|         console.error('Error adding block:', err); | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| import { API, BlockTool, BlockToolData } from '@editorjs/editorjs'; | ||||
| import './checklist.css'; | ||||
| import { EditorJSPlugin, CustomEditorJSPlugin, EditorJSPluginData } from './types'; | ||||
| 
 | ||||
| interface ChecklistItem { | ||||
|   text: string; | ||||
| @ -20,8 +21,8 @@ interface ChecklistConfig { | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| class ChecklistTool implements BlockTool { | ||||
|     private data: ChecklistData; | ||||
| class ChecklistTool implements EditorJSPlugin { | ||||
|     protected data: ChecklistData; | ||||
|     private wrapper: HTMLElement; | ||||
|     private api: API; | ||||
|     private readOnly: boolean; | ||||
| @ -29,6 +30,11 @@ class ChecklistTool implements BlockTool { | ||||
|     private itemElements: HTMLElement[] = []; | ||||
|     private pendingUpdate = false; | ||||
|    | ||||
|     static = { | ||||
|       toolbox: ChecklistTool.toolbox, | ||||
|       sanitize: ChecklistTool.sanitize | ||||
|     }; | ||||
|    | ||||
|     static get toolbox() { | ||||
|       return { | ||||
|         title: 'Checklist', | ||||
| @ -346,21 +352,23 @@ class ChecklistTool implements BlockTool { | ||||
|       void this.notifyChange(); | ||||
|     } | ||||
|    | ||||
|     save(): BlockToolData<ChecklistData> { | ||||
|     save(): { data: ChecklistData } { | ||||
|       return { | ||||
|         items: this.data.items.map(item => ({ | ||||
|           text: item.text || '', | ||||
|           checked: Boolean(item.checked) | ||||
|         })) | ||||
|         data: { | ||||
|           items: this.data.items.map(item => ({ | ||||
|             text: item.text || '', | ||||
|             checked: Boolean(item.checked) | ||||
|           })) | ||||
|         } | ||||
|       }; | ||||
|     } | ||||
|    | ||||
|     validate(savedData: BlockToolData<ChecklistData>): boolean { | ||||
|     validate(savedData: { data: ChecklistData }): boolean { | ||||
|       try { | ||||
|         const { items } = savedData; | ||||
|         if (!Array.isArray(items)) return false; | ||||
|         const { data } = savedData; | ||||
|         if (!data || !Array.isArray(data.items)) return false; | ||||
|          | ||||
|         return items.every(item =>  | ||||
|         return data.items.every(item =>  | ||||
|           item && | ||||
|           typeof item === 'object' && | ||||
|           'text' in item && | ||||
| @ -382,8 +390,32 @@ class ChecklistTool implements BlockTool { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| class ReactChecklistTool extends ChecklistTool { | ||||
| class ReactChecklistTool extends ChecklistTool implements CustomEditorJSPlugin { | ||||
|   private settings: { title: string }; | ||||
|   static = { | ||||
|     toolbox: ReactChecklistTool.toolbox, | ||||
|     sanitize: ReactChecklistTool.sanitize | ||||
|   }; | ||||
| 
 | ||||
|   static renderNewElementTile(onAdd?: () => void): React.ReactElement { | ||||
|     return ( | ||||
|       <button  | ||||
|         className="w-full border rounded-md p-4 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors text-left" | ||||
|         role="button" | ||||
|         aria-label="Add new checklist block" | ||||
|         onClick={onAdd} | ||||
|       > | ||||
|         <div className="flex items-center gap-2 mb-2"> | ||||
|           <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||||
|             <path d="M9 11L12 14L20 6" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/> | ||||
|             <path d="M20 12V18C20 19.1046 19.1046 20 18 20H6C4.89543 20 4 19.1046 4 18V6C4 4.89543 4.89543 4 6 4H15" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/> | ||||
|           </svg> | ||||
|         </div> | ||||
|         <h3 className="text-base font-medium">Checklist</h3> | ||||
|         <p className="text-sm text-gray-500 mt-1">Add a new checklist</p> | ||||
|       </button> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   constructor(config: ChecklistConfig) { | ||||
|     super(config); | ||||
| @ -392,6 +424,17 @@ class ReactChecklistTool extends ChecklistTool { | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|   save(): { data: ChecklistData } { | ||||
|     return { | ||||
|       data: { | ||||
|         items: this.data.items.map(item => ({ | ||||
|           text: item.text || '', | ||||
|           checked: Boolean(item.checked) | ||||
|         })) | ||||
|       } | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|   public renderSettings(): HTMLElement { | ||||
|     const wrapper = document.createElement('div'); | ||||
|     wrapper.innerHTML = ` | ||||
| @ -411,24 +454,8 @@ class ReactChecklistTool extends ChecklistTool { | ||||
|     return wrapper; | ||||
|   } | ||||
| 
 | ||||
|   public static renderNewElementTile(onAdd?: () => void): React.ReactElement { | ||||
|     return ( | ||||
|       <button  | ||||
|         className="w-full border rounded-md p-4 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors text-left" | ||||
|         role="button" | ||||
|         aria-label="Add new checklist block" | ||||
|         onClick={onAdd} | ||||
|       > | ||||
|         <div className="flex items-center gap-2 mb-2"> | ||||
|           <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||||
|             <path d="M9 11L12 14L20 6" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/> | ||||
|             <path d="M20 12V18C20 19.1046 19.1046 20 18 20H6C4.89543 20 4 19.1046 4 18V6C4 4.89543 4.89543 4 6 4H15" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/> | ||||
|           </svg> | ||||
|         </div> | ||||
|         <h3 className="text-base font-medium">Checklist</h3> | ||||
|         <p className="text-sm text-gray-500 mt-1">Add a new checklist</p> | ||||
|       </button> | ||||
|     ); | ||||
|   public renderNewElementTile(onAdd?: () => void): React.ReactElement { | ||||
|     return ReactChecklistTool.renderNewElementTile(onAdd); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										41
									
								
								src/types.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/types.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | ||||
| import { BlockTool, PasteEvent } from '@editorjs/editorjs'; | ||||
| 
 | ||||
| export interface EditorJSPluginConfig<T = any> { | ||||
|   data?: T; | ||||
|   config?: { | ||||
|     placeholder?: string; | ||||
|     [key: string]: any; | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export interface EditorJSPluginData<T = any> { | ||||
|   data: T; | ||||
| } | ||||
| 
 | ||||
| export interface EditorJSPlugin<T = any> extends BlockTool { | ||||
|   // Required methods from BlockTool
 | ||||
|   save(): EditorJSPluginData<T>; | ||||
|   validate(savedData: T): boolean; | ||||
|   render(): HTMLElement; | ||||
| 
 | ||||
|   // Optional methods
 | ||||
|   renderSettings?(): HTMLElement; | ||||
|   onPaste?(event: PasteEvent): void; | ||||
|   merge?(data: T): T; | ||||
|    | ||||
|   // Static properties
 | ||||
|   static: { | ||||
|     toolbox: { | ||||
|       title: string; | ||||
|       icon: string; | ||||
|     }; | ||||
|     sanitize: { | ||||
|       [key: string]: boolean | { [key: string]: boolean }; | ||||
|     }; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export interface CustomEditorJSPlugin<T = any> extends EditorJSPlugin<T> { | ||||
|   renderNewElementTile(onAdd?: () => void): React.ReactElement; | ||||
|   renderSettings(): HTMLElement; | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user