11import { useMemo , useState } from "react" ;
2- import {
3- Block ,
4- BlockNoteEditor ,
5- BlockSchema ,
6- PartialBlock ,
7- } from "@blocknote/core" ;
2+ import { BlockNoteEditor , BlockSchema } from "@blocknote/core" ;
83import { IconType } from "react-icons" ;
94import {
105 RiH1 ,
@@ -15,114 +10,112 @@ import {
1510 RiText ,
1611} from "react-icons/ri" ;
1712
18- import {
19- ToolbarDropdown ,
20- ToolbarDropdownProps ,
21- } from "../../../SharedComponents/Toolbar/components/ToolbarDropdown" ;
13+ import { ToolbarDropdown } from "../../../SharedComponents/Toolbar/components/ToolbarDropdown" ;
2214import { useEditorSelectionChange } from "../../../hooks/useEditorSelectionChange" ;
2315import { useEditorContentChange } from "../../../hooks/useEditorContentChange" ;
16+ import { ToolbarDropdownItemProps } from "../../../SharedComponents/Toolbar/components/ToolbarDropdownItem" ;
2417
25- type HeadingLevels = "1" | "2" | "3" ;
26-
27- const headingIcons : Record < HeadingLevels , IconType > = {
28- "1" : RiH1 ,
29- "2" : RiH2 ,
30- "3" : RiH3 ,
18+ export type BlockTypeDropdownItem = {
19+ name : string ;
20+ type : string ;
21+ props ?: Record < string , string > ;
22+ icon : IconType ;
3123} ;
3224
33- const shouldShow = < BSchema extends BlockSchema > ( block : Block < BSchema > ) => {
34- if ( block . type === "paragraph" ) {
35- return true ;
36- }
37-
38- if ( block . type === "heading" && "level" in block . props ) {
39- return true ;
40- }
41-
42- if ( block . type === "bulletListItem" ) {
43- return true ;
44- }
45-
46- return block . type === "numberedListItem" ;
47- } ;
25+ export const defaultBlockTypeDropdownItems : BlockTypeDropdownItem [ ] = [
26+ {
27+ name : "Paragraph" ,
28+ type : "paragraph" ,
29+ icon : RiText ,
30+ } ,
31+ {
32+ name : "Heading 1" ,
33+ type : "heading" ,
34+ props : { level : "1" } ,
35+ icon : RiH1 ,
36+ } ,
37+ {
38+ name : "Heading 2" ,
39+ type : "heading" ,
40+ props : { level : "2" } ,
41+ icon : RiH2 ,
42+ } ,
43+ {
44+ name : "Heading 3" ,
45+ type : "heading" ,
46+ props : { level : "3" } ,
47+ icon : RiH3 ,
48+ } ,
49+ {
50+ name : "Bullet List" ,
51+ type : "bulletListItem" ,
52+ icon : RiListUnordered ,
53+ } ,
54+ {
55+ name : "Numbered List" ,
56+ type : "numberedListItem" ,
57+ icon : RiListOrdered ,
58+ } ,
59+ ] ;
4860
4961export const BlockTypeDropdown = < BSchema extends BlockSchema > ( props : {
5062 editor : BlockNoteEditor < BSchema > ;
63+ items ?: BlockTypeDropdownItem [ ] ;
5164} ) => {
5265 const [ block , setBlock ] = useState (
5366 props . editor . getTextCursorPosition ( ) . block
5467 ) ;
5568
56- const dropdownItems : ToolbarDropdownProps [ "items" ] = useMemo ( ( ) => {
57- const items : ToolbarDropdownProps [ "items" ] = [ ] ;
58-
59- if ( "paragraph" in props . editor . schema ) {
60- items . push ( {
61- onClick : ( ) => {
62- props . editor . focus ( ) ;
63- props . editor . updateBlock ( block , {
64- type : "paragraph" ,
65- props : { } ,
66- } ) ;
67- } ,
68- text : "Paragraph" ,
69- icon : RiText ,
70- isSelected : block . type === "paragraph" ,
71- } ) ;
72- }
73-
74- if (
75- "heading" in props . editor . schema &&
76- "level" in props . editor . schema . heading . propSchema
77- ) {
78- items . push (
79- ...( [ "1" , "2" , "3" ] as const ) . map ( ( level ) => ( {
80- onClick : ( ) => {
81- props . editor . focus ( ) ;
82- props . editor . updateBlock ( block , {
83- type : "heading" ,
84- props : { level : level } ,
85- } as PartialBlock < BSchema > ) ;
86- } ,
87- text : "Heading " + level ,
88- icon : headingIcons [ level ] ,
89- isSelected : block . type === "heading" && block . props . level === level ,
90- } ) )
91- ) ;
92- }
93-
94- if ( "bulletListItem" in props . editor . schema ) {
95- items . push ( {
96- onClick : ( ) => {
97- props . editor . focus ( ) ;
98- props . editor . updateBlock ( block , {
99- type : "bulletListItem" ,
100- props : { } ,
101- } ) ;
102- } ,
103- text : "Bullet List" ,
104- icon : RiListUnordered ,
105- isSelected : block . type === "bulletListItem" ,
106- } ) ;
107- }
69+ const filteredItems : BlockTypeDropdownItem [ ] = useMemo ( ( ) => {
70+ return ( props . items || defaultBlockTypeDropdownItems ) . filter ( ( item ) => {
71+ // Checks if block type exists in the schema
72+ if ( ! ( item . type in props . editor . schema ) ) {
73+ return false ;
74+ }
75+
76+ // Checks if props for the block type are valid
77+ for ( const [ prop , value ] of Object . entries ( item . props || { } ) ) {
78+ const propSchema = props . editor . schema [ item . type ] . propSchema ;
79+
80+ // Checks if the prop exists for the block type
81+ if ( ! ( prop in propSchema ) ) {
82+ return false ;
83+ }
84+
85+ // Checks if the prop's value is valid
86+ if (
87+ propSchema [ prop ] . values !== undefined &&
88+ ! propSchema [ prop ] . values ! . includes ( value )
89+ ) {
90+ return false ;
91+ }
92+ }
93+
94+ return true ;
95+ } ) ;
96+ } , [ props . editor , props . items ] ) ;
97+
98+ const shouldShow : boolean = useMemo (
99+ ( ) => filteredItems . find ( ( item ) => item . type === block . type ) !== undefined ,
100+ [ block . type , filteredItems ]
101+ ) ;
108102
109- if ( "numberedListItem" in props . editor . schema ) {
110- items . push ( {
103+ const fullItems : ToolbarDropdownItemProps [ ] = useMemo (
104+ ( ) =>
105+ filteredItems . map ( ( item ) => ( {
106+ text : item . name ,
107+ icon : item . icon ,
111108 onClick : ( ) => {
112109 props . editor . focus ( ) ;
113110 props . editor . updateBlock ( block , {
114- type : "numberedListItem" ,
111+ type : item . type ,
115112 props : { } ,
116113 } ) ;
117114 } ,
118- text : "Numbered List" ,
119- icon : RiListOrdered ,
120- isSelected : block . type === "numberedListItem" ,
121- } ) ;
122- }
123-
124- return items ;
125- } , [ block , props . editor ] ) ;
115+ isSelected : block . type === item . type ,
116+ } ) ) ,
117+ [ block , filteredItems , props . editor ]
118+ ) ;
126119
127120 useEditorContentChange ( props . editor , ( ) => {
128121 setBlock ( props . editor . getTextCursorPosition ( ) . block ) ;
@@ -132,9 +125,9 @@ export const BlockTypeDropdown = <BSchema extends BlockSchema>(props: {
132125 setBlock ( props . editor . getTextCursorPosition ( ) . block ) ;
133126 } ) ;
134127
135- if ( ! shouldShow ( block ) ) {
128+ if ( ! shouldShow ) {
136129 return null ;
137130 }
138131
139- return < ToolbarDropdown items = { dropdownItems } /> ;
132+ return < ToolbarDropdown items = { fullItems } /> ;
140133} ;
0 commit comments