66 useRef ,
77 useLayoutEffect ,
88} from "react" ;
9- import { SlashCommandItem } from "./slash-command" ;
9+ import { SlashCommandGroup , SlashCommandItem } from "./slash-command" ;
1010//https://github.com/steven-tey/novel from steven-tey helped a lot with this implmentation
1111
1212export const updateScrollView = ( container : HTMLElement , item : HTMLElement ) => {
@@ -17,9 +17,9 @@ export const updateScrollView = (container: HTMLElement, item: HTMLElement) => {
1717 const bottom = top + itemHeight ;
1818
1919 if ( top < container . scrollTop ) {
20- container . scrollTop -= container . scrollTop - top + 5 ;
20+ container . scrollTop -= container . scrollTop - top + 15 ;
2121 } else if ( bottom > containerHeight + container . scrollTop ) {
22- container . scrollTop += bottom - containerHeight - container . scrollTop + 5 ;
22+ container . scrollTop += bottom - containerHeight - container . scrollTop + 15 ;
2323 }
2424} ;
2525
@@ -30,17 +30,18 @@ export const CommandMenu = React.forwardRef(
3030 command,
3131 editor,
3232 } : {
33- items : SlashCommandItem [ ] ;
33+ items : SlashCommandGroup [ ] ;
3434 command : ( item : SlashCommandItem ) => void ;
3535 editor : any ;
3636 } ,
3737 ref
3838 ) => {
3939 const [ selectedIndex , setSelectedIndex ] = useState ( 0 ) ;
40+ const [ groupIndex , setGroupIndex ] = useState ( 0 ) ;
4041
4142 const selectItem = useCallback (
42- ( index : number ) => {
43- const item = items [ index ] ;
43+ ( index : number , groupIndex : number ) => {
44+ const item = items [ groupIndex ] ?. commands [ index ] ;
4445 if ( item ) {
4546 command ( item ) ;
4647 }
@@ -51,11 +52,11 @@ export const CommandMenu = React.forwardRef(
5152 React . useImperativeHandle ( ref , ( ) => ( {
5253 onKeyDown : ( { event } : { event : React . KeyboardEvent } ) => {
5354 if ( event . key === "Enter" ) {
54- if ( ! items . length || selectedIndex === - 1 ) {
55+ if ( ! items . length || selectedIndex === - 1 || groupIndex === - 1 ) {
5556 return false ;
5657 }
5758
58- selectItem ( selectedIndex ) ;
59+ selectItem ( selectedIndex , groupIndex ) ;
5960
6061 return true ;
6162 }
@@ -68,12 +69,44 @@ export const CommandMenu = React.forwardRef(
6869 const onKeyDown = ( e : KeyboardEvent ) => {
6970 if ( e . key === "ArrowUp" ) {
7071 e . preventDefault ( ) ;
71- setSelectedIndex ( ( selectedIndex + items . length - 1 ) % items . length ) ;
72+ let newCommandIndex = selectedIndex - 1 ;
73+ let newGroupIndex = groupIndex ;
74+
75+ if ( newCommandIndex < 0 ) {
76+ newGroupIndex = groupIndex - 1 ;
77+ newCommandIndex = items [ newGroupIndex ] ?. commands . length - 1 || 0 ;
78+ }
79+
80+ if ( newGroupIndex < 0 ) {
81+ newGroupIndex = items . length - 1 ;
82+ newCommandIndex = items [ newGroupIndex ] . commands . length - 1 ;
83+ }
84+
85+ setSelectedIndex ( newCommandIndex ) ;
86+ setGroupIndex ( newGroupIndex ) ;
87+
7288 return true ;
7389 }
7490 if ( e . key === "ArrowDown" ) {
7591 e . preventDefault ( ) ;
76- setSelectedIndex ( ( selectedIndex + 1 ) % items . length ) ;
92+
93+ const commands = items [ groupIndex ] . commands ;
94+
95+ let newCommandIndex = selectedIndex + 1 ;
96+ let newGroupIndex = groupIndex ;
97+
98+ if ( commands . length - 1 < newCommandIndex ) {
99+ newCommandIndex = 0 ;
100+ newGroupIndex = groupIndex + 1 ;
101+ }
102+
103+ if ( items . length - 1 < newGroupIndex ) {
104+ newGroupIndex = 0 ;
105+ }
106+
107+ setSelectedIndex ( newCommandIndex ) ;
108+ setGroupIndex ( newGroupIndex ) ;
109+
77110 return true ;
78111 }
79112 return false ;
@@ -86,44 +119,60 @@ export const CommandMenu = React.forwardRef(
86119
87120 useEffect ( ( ) => {
88121 setSelectedIndex ( 0 ) ;
122+ setGroupIndex ( 0 ) ;
89123 } , [ items ] ) ;
90124
91125 const commandListContainer = useRef < HTMLDivElement > ( null ) ;
92126
93127 useLayoutEffect ( ( ) => {
94128 const container = commandListContainer ?. current ;
95129
96- const item = container ?. children [ selectedIndex ] as HTMLElement ;
130+ const item = container ?. children [ groupIndex ] ?. children [ selectedIndex ] ;
97131
98- if ( item && container ) updateScrollView ( container , item ) ;
132+ if ( container && item ) {
133+ updateScrollView ( container , item as HTMLElement ) ;
134+ }
99135 } , [ selectedIndex ] ) ;
100136
101137 return items . length > 0 ? (
102138 < div
103139 id = "slash-command"
104140 ref = { commandListContainer }
105- className = "eddies-z-50 eddies-h-auto eddies-max-h-[330px] eddies-w-72 eddies-overflow-y-auto eddies-rounded-md eddies-bg-color-bg eddies-px-1 eddies-py-2 eddies-transition-all"
141+ className = "eddies-z-50 eddies-h-auto eddies-max-h-[330px] eddies-w-72 eddies-overflow-y-auto eddies-rounded-md eddies-bg-color-bg-secondary eddies-px-2 eddies-py-3 eddies-transition-all"
106142 >
107- { items . map ( ( item : SlashCommandItem , index : number ) => {
143+ { items . map ( ( group : SlashCommandGroup , mainIndex : number ) => {
108144 return (
109- < button
110- className = { `eddies-flex eddies-w-full eddies-items-center eddies-space-x-2 eddies-rounded-md eddies-px-2 eddies-py-1 eddies-text-left eddies-text-sm eddies-text-color-text hover:eddies-bg-color-bg-secondary ${
111- index === selectedIndex
112- ? "eddies-bg-stone-100 eddies-text-color-text-secondary"
113- : ""
114- } `}
115- key = { index }
116- onClick = { ( ) => selectItem ( index ) }
117- >
118- < div className = "eddies-flex eddies-h-10 eddies-w-10 eddies-items-center eddies-justify-center eddies-rounded-md eddies-border eddies-border-border eddies-bg-color-bg-secondary" >
119- { /* @ts -ignore */ }
120- < item . icon className = { "eddies-h-5.5 eddies-w-5.5" } />
121- </ div >
122- < div >
123- < p className = "eddies-font-medium" > { item . title } </ p >
124- < p className = "eddies-text-xs" > { item . description } </ p >
145+ < div key = { mainIndex } className = "eddies-flex eddies-flex-col" >
146+ < p className = "eddies-text-xs eddies-font-medium eddies-text-color-text-secondary eddies-pl-2.5" >
147+ { group . title }
148+ </ p >
149+ < div className = "eddies-mt-2" >
150+ { group . commands . map ( ( item , index ) => (
151+ < button
152+ className = { `eddies-mb-2 eddies-flex eddies-w-full eddies-items-center eddies-space-x-2 eddies-rounded-[4px] eddies-px-2.5 eddies-py-1.5 eddies-text-left eddies-text-sm eddies-text-color-text ${
153+ index === selectedIndex && groupIndex === mainIndex
154+ ? "eddies-bg-color-bg eddies-text-color-text-secondary"
155+ : "hover:eddies-bg-color-bg"
156+ } `}
157+ key = { index }
158+ onClick = { ( ) => selectItem ( index , groupIndex ) }
159+ >
160+ < div className = "eddies-flex eddies-h-10 eddies-w-10 eddies-items-center eddies-justify-center eddies-rounded-[4px] eddies-bg-color-bg" >
161+ { /* @ts -ignore */ }
162+ < item . icon className = { "eddies-h-5.5 eddies-w-5.5" } />
163+ </ div >
164+ < div >
165+ < p className = "eddies-text-base eddies-font-medium" >
166+ { item . title }
167+ </ p >
168+ < p className = "eddies-text-xs eddies-font-light" >
169+ { item . description }
170+ </ p >
171+ </ div >
172+ </ button >
173+ ) ) }
125174 </ div >
126- </ button >
175+ </ div >
127176 ) ;
128177 } ) }
129178 </ div >
0 commit comments