11import {
22 Block ,
3+ BlockNoteEditor ,
34 BlockSchema ,
4- Dictionary ,
5+ editorHasBlockWithType ,
56 InlineContentSchema ,
67 StyleSchema ,
78} from "@blocknote/core" ;
@@ -29,7 +30,6 @@ import {
2930import { useBlockNoteEditor } from "../../../hooks/useBlockNoteEditor.js" ;
3031import { useEditorContentOrSelectionChange } from "../../../hooks/useEditorContentOrSelectionChange.js" ;
3132import { useSelectedBlocks } from "../../../hooks/useSelectedBlocks.js" ;
32- import { useDictionary } from "../../../i18n/dictionary.js" ;
3333
3434export type BlockTypeSelectItem = {
3535 name : string ;
@@ -41,146 +41,123 @@ export type BlockTypeSelectItem = {
4141 ) => boolean ;
4242} ;
4343
44- export const blockTypeSelectItems = (
45- dict : Dictionary ,
46- ) : BlockTypeSelectItem [ ] => [
47- {
48- name : dict . slash_menu . paragraph . title ,
49- type : "paragraph" ,
50- icon : RiText ,
51- isSelected : ( block ) => block . type === "paragraph" ,
52- } ,
53- {
54- name : dict . slash_menu . heading . title ,
55- type : "heading" ,
56- props : { level : 1 } ,
57- icon : RiH1 ,
58- isSelected : ( block ) =>
59- block . type === "heading" &&
60- "level" in block . props &&
61- block . props . level === 1 ,
62- } ,
63- {
64- name : dict . slash_menu . heading_2 . title ,
65- type : "heading" ,
66- props : { level : 2 } ,
67- icon : RiH2 ,
68- isSelected : ( block ) =>
69- block . type === "heading" &&
70- "level" in block . props &&
71- block . props . level === 2 ,
72- } ,
73- {
74- name : dict . slash_menu . heading_3 . title ,
75- type : "heading" ,
76- props : { level : 3 } ,
77- icon : RiH3 ,
78- isSelected : ( block ) =>
79- block . type === "heading" &&
80- "level" in block . props &&
81- block . props . level === 3 ,
82- } ,
83- {
84- name : dict . slash_menu . heading_4 . title ,
85- type : "heading" ,
86- props : { level : 4 } ,
87- icon : RiH4 ,
88- isSelected : ( block ) =>
89- block . type === "heading" &&
90- "level" in block . props &&
91- block . props . level === 4 ,
92- } ,
93- {
94- name : dict . slash_menu . heading_5 . title ,
95- type : "heading" ,
96- props : { level : 5 } ,
97- icon : RiH5 ,
98- isSelected : ( block ) =>
99- block . type === "heading" &&
100- "level" in block . props &&
101- block . props . level === 5 ,
102- } ,
103- {
104- name : dict . slash_menu . heading_6 . title ,
105- type : "heading" ,
106- props : { level : 6 } ,
107- icon : RiH6 ,
108- isSelected : ( block ) =>
109- block . type === "heading" &&
110- "level" in block . props &&
111- block . props . level === 6 ,
112- } ,
113- {
114- name : dict . slash_menu . toggle_heading . title ,
115- type : "heading" ,
116- props : { level : 1 , isToggleable : true } ,
117- icon : RiH1 ,
118- isSelected : ( block ) =>
119- block . type === "heading" &&
120- "level" in block . props &&
121- block . props . level === 1 &&
122- "isToggleable" in block . props &&
123- block . props . isToggleable ,
124- } ,
125- {
126- name : dict . slash_menu . toggle_heading_2 . title ,
127- type : "heading" ,
128- props : { level : 2 , isToggleable : true } ,
129- icon : RiH2 ,
130- isSelected : ( block ) =>
131- block . type === "heading" &&
132- "level" in block . props &&
133- block . props . level === 2 &&
134- "isToggleable" in block . props &&
135- block . props . isToggleable ,
136- } ,
137- {
138- name : dict . slash_menu . toggle_heading_3 . title ,
139- type : "heading" ,
140- props : { level : 3 , isToggleable : true } ,
141- icon : RiH3 ,
142- isSelected : ( block ) =>
143- block . type === "heading" &&
144- "level" in block . props &&
145- block . props . level === 3 &&
146- "isToggleable" in block . props &&
147- block . props . isToggleable ,
148- } ,
149- {
150- name : dict . slash_menu . quote . title ,
151- type : "quote" ,
152- icon : RiQuoteText ,
153- isSelected : ( block ) => block . type === "quote" ,
154- } ,
155- {
156- name : dict . slash_menu . toggle_list . title ,
157- type : "toggleListItem" ,
158- icon : RiPlayList2Fill ,
159- isSelected : ( block ) => block . type === "toggleListItem" ,
160- } ,
161- {
162- name : dict . slash_menu . bullet_list . title ,
163- type : "bulletListItem" ,
164- icon : RiListUnordered ,
165- isSelected : ( block ) => block . type === "bulletListItem" ,
166- } ,
167- {
168- name : dict . slash_menu . numbered_list . title ,
169- type : "numberedListItem" ,
170- icon : RiListOrdered ,
171- isSelected : ( block ) => block . type === "numberedListItem" ,
172- } ,
173- {
174- name : dict . slash_menu . check_list . title ,
175- type : "checkListItem" ,
176- icon : RiListCheck3 ,
177- isSelected : ( block ) => block . type === "checkListItem" ,
178- } ,
179- ] ;
44+ const headingLevelIcons : Record < any , IconType > = {
45+ 1 : RiH1 ,
46+ 2 : RiH2 ,
47+ 3 : RiH3 ,
48+ 4 : RiH4 ,
49+ 5 : RiH5 ,
50+ 6 : RiH6 ,
51+ } ;
52+
53+ export function getDefaultBlockTypeSelectItems <
54+ BSchema extends BlockSchema ,
55+ I extends InlineContentSchema ,
56+ S extends StyleSchema ,
57+ > ( editor : BlockNoteEditor < BSchema , I , S > ) {
58+ const items : BlockTypeSelectItem [ ] = [ ] ;
59+
60+ if ( editorHasBlockWithType ( editor , "paragraph" ) ) {
61+ items . push ( {
62+ name : editor . dictionary . slash_menu . paragraph . title ,
63+ type : "paragraph" ,
64+ icon : RiText ,
65+ isSelected : ( block ) => block . type === "paragraph" ,
66+ } ) ;
67+ }
68+
69+ if ( editorHasBlockWithType ( editor , "heading" , { level : "number" } ) ) {
70+ (
71+ editor . schema . blockSchema . heading . propSchema . level . values || [ 1 , 2 , 3 ]
72+ ) . forEach ( ( level ) => {
73+ items . push ( {
74+ name : editor . dictionary . slash_menu [
75+ `heading${ level === 1 ? "" : "_" + level } ` as keyof typeof editor . dictionary . slash_menu
76+ ] . title ,
77+ type : "heading" ,
78+ props : { level } ,
79+ icon : headingLevelIcons [ level ] ,
80+ isSelected : ( block ) =>
81+ block . type === "heading" &&
82+ "level" in block . props &&
83+ block . props . level === level ,
84+ } ) ;
85+ } ) ;
86+ }
87+
88+ if (
89+ editorHasBlockWithType ( editor , "heading" , {
90+ level : "number" ,
91+ isToggleable : "boolean" ,
92+ } )
93+ ) {
94+ ( editor . schema . blockSchema . heading . propSchema . level . values || [ 1 , 2 , 3 ] )
95+ . filter ( ( level ) => level <= 3 )
96+ . forEach ( ( level ) => {
97+ items . push ( {
98+ name : editor . dictionary . slash_menu [
99+ `toggle_heading${ level === 1 ? "" : "_" + level } ` as keyof typeof editor . dictionary . slash_menu
100+ ] . title ,
101+ type : "heading" ,
102+ props : { level, isToggleable : true } ,
103+ icon : headingLevelIcons [ level ] ,
104+ isSelected : ( block ) =>
105+ block . type === "heading" &&
106+ "level" in block . props &&
107+ block . props . level === level &&
108+ "isToggleable" in block . props &&
109+ block . props . isToggleable ,
110+ } ) ;
111+ } ) ;
112+ }
113+
114+ if ( editorHasBlockWithType ( editor , "quote" ) ) {
115+ items . push ( {
116+ name : editor . dictionary . slash_menu . quote . title ,
117+ type : "quote" ,
118+ icon : RiQuoteText ,
119+ isSelected : ( block ) => block . type === "quote" ,
120+ } ) ;
121+ }
122+
123+ if ( editorHasBlockWithType ( editor , "toggleListItem" ) ) {
124+ items . push ( {
125+ name : editor . dictionary . slash_menu . toggle_list . title ,
126+ type : "toggleListItem" ,
127+ icon : RiPlayList2Fill ,
128+ isSelected : ( block ) => block . type === "toggleListItem" ,
129+ } ) ;
130+ }
131+ if ( editorHasBlockWithType ( editor , "bulletListItem" ) ) {
132+ items . push ( {
133+ name : editor . dictionary . slash_menu . bullet_list . title ,
134+ type : "bulletListItem" ,
135+ icon : RiListUnordered ,
136+ isSelected : ( block ) => block . type === "bulletListItem" ,
137+ } ) ;
138+ }
139+ if ( editorHasBlockWithType ( editor , "numberedListItem" ) ) {
140+ items . push ( {
141+ name : editor . dictionary . slash_menu . numbered_list . title ,
142+ type : "numberedListItem" ,
143+ icon : RiListOrdered ,
144+ isSelected : ( block ) => block . type === "numberedListItem" ,
145+ } ) ;
146+ }
147+ if ( editorHasBlockWithType ( editor , "checkListItem" ) ) {
148+ items . push ( {
149+ name : editor . dictionary . slash_menu . check_list . title ,
150+ type : "checkListItem" ,
151+ icon : RiListCheck3 ,
152+ isSelected : ( block ) => block . type === "checkListItem" ,
153+ } ) ;
154+ }
155+
156+ return items ;
157+ }
180158
181159export const BlockTypeSelect = ( props : { items ?: BlockTypeSelectItem [ ] } ) => {
182160 const Components = useComponentsContext ( ) ! ;
183- const dict = useDictionary ( ) ;
184161
185162 const editor = useBlockNoteEditor <
186163 BlockSchema ,
@@ -193,10 +170,8 @@ export const BlockTypeSelect = (props: { items?: BlockTypeSelectItem[] }) => {
193170 const [ block , setBlock ] = useState ( editor . getTextCursorPosition ( ) . block ) ;
194171
195172 const filteredItems : BlockTypeSelectItem [ ] = useMemo ( ( ) => {
196- return ( props . items || blockTypeSelectItems ( dict ) ) . filter (
197- ( item ) => item . type in editor . schema . blockSchema ,
198- ) ;
199- } , [ editor , dict , props . items ] ) ;
173+ return props . items || getDefaultBlockTypeSelectItems ( editor ) ;
174+ } , [ editor , props . items ] ) ;
200175
201176 const shouldShow : boolean = useMemo (
202177 ( ) => filteredItems . find ( ( item ) => item . type === block . type ) !== undefined ,
0 commit comments