@@ -66,6 +66,7 @@ import {
6666 $setSelection ,
6767} from 'lexical' ;
6868import {
69+ $createTableSelection ,
6970 $deleteTableColumn__EXPERIMENTAL ,
7071 $deleteTableRow__EXPERIMENTAL ,
7172 $getTableCellNodeFromLexicalNode ,
@@ -155,6 +156,12 @@ type SelectionSnapshot =
155156 focusType : 'text' | 'element' ;
156157 }
157158 | { type : 'node' ; keys : string [ ] }
159+ | {
160+ type : 'table' ;
161+ tableKey : string ;
162+ anchorCellKey : string ;
163+ focusCellKey : string ;
164+ }
158165 | null ;
159166
160167const RICH_TEXT_THEME = {
@@ -504,6 +511,7 @@ const ToolbarPlugin: React.FC<{
504511 const [ isInTable , setIsInTable ] = useState ( false ) ;
505512 const [ hasHeaderRow , setHasHeaderRow ] = useState ( false ) ;
506513 const pendingLinkSelectionRef = useRef < SelectionSnapshot > ( null ) ;
514+ const pendingTableSelectionRef = useRef < SelectionSnapshot > ( null ) ;
507515 const closeLinkModal = useCallback ( ( ) => {
508516 setIsLinkModalOpen ( false ) ;
509517 } , [ ] ) ;
@@ -513,27 +521,43 @@ const ToolbarPlugin: React.FC<{
513521 } , [ closeLinkModal ] ) ;
514522
515523 const restoreSelectionFromSnapshot = useCallback (
516- ( snapshot : SelectionSnapshot = pendingLinkSelectionRef . current ) => {
517- if ( ! snapshot ) {
524+ ( snapshot : SelectionSnapshot | null | undefined = pendingLinkSelectionRef . current ) => {
525+ const snapshotToUse = snapshot ?? pendingLinkSelectionRef . current ;
526+
527+ if ( ! snapshotToUse ) {
518528 return null ;
519529 }
520530
521- if ( snapshot . type === 'range' ) {
531+ if ( snapshotToUse . type === 'table' ) {
532+ const selection = $createTableSelection ( ) ;
533+ const tableNode = $getNodeByKey ( snapshotToUse . tableKey ) ;
534+ const anchorCell = $getNodeByKey ( snapshotToUse . anchorCellKey ) ;
535+ const focusCell = $getNodeByKey ( snapshotToUse . focusCellKey ) ;
536+
537+ if ( ! tableNode || ! anchorCell || ! focusCell ) {
538+ return null ;
539+ }
540+
541+ selection . set ( snapshotToUse . tableKey , snapshotToUse . anchorCellKey , snapshotToUse . focusCellKey ) ;
542+ return selection ;
543+ }
544+
545+ if ( snapshotToUse . type === 'range' ) {
522546 const selection = $createRangeSelection ( ) ;
523- const anchorNode = $getNodeByKey ( snapshot . anchorKey ) ;
524- const focusNode = $getNodeByKey ( snapshot . focusKey ) ;
547+ const anchorNode = $getNodeByKey ( snapshotToUse . anchorKey ) ;
548+ const focusNode = $getNodeByKey ( snapshotToUse . focusKey ) ;
525549
526550 if ( ! anchorNode || ! focusNode ) {
527551 return null ;
528552 }
529553
530- selection . anchor . set ( snapshot . anchorKey , snapshot . anchorOffset , snapshot . anchorType ) ;
531- selection . focus . set ( snapshot . focusKey , snapshot . focusOffset , snapshot . focusType ) ;
554+ selection . anchor . set ( snapshotToUse . anchorKey , snapshotToUse . anchorOffset , snapshotToUse . anchorType ) ;
555+ selection . focus . set ( snapshotToUse . focusKey , snapshotToUse . focusOffset , snapshotToUse . focusType ) ;
532556 return selection ;
533557 }
534558
535559 const selection = $createNodeSelection ( ) ;
536- snapshot . keys . forEach ( key => {
560+ snapshotToUse . keys . forEach ( key => {
537561 const node = $getNodeByKey ( key ) ;
538562 if ( node ) {
539563 selection . add ( node . getKey ( ) ) ;
@@ -729,6 +753,54 @@ const ToolbarPlugin: React.FC<{
729753 return true ;
730754 } , [ editor ] ) ;
731755
756+ const captureTableSelection = useCallback ( ( ) => {
757+ let snapshot : SelectionSnapshot = null ;
758+
759+ editor . getEditorState ( ) . read ( ( ) => {
760+ const selection = $getSelection ( ) ;
761+
762+ if ( $isTableSelection ( selection ) ) {
763+ const anchorCell = $getTableCellNodeFromLexicalNode ( selection . anchor . getNode ( ) ) ;
764+ const focusCell = $getTableCellNodeFromLexicalNode ( selection . focus . getNode ( ) ) ;
765+
766+ if ( ! anchorCell || ! focusCell ) {
767+ return ;
768+ }
769+
770+ const tableNode = $getTableNodeFromLexicalNodeOrThrow ( anchorCell ) ;
771+ snapshot = {
772+ type : 'table' ,
773+ tableKey : tableNode . getKey ( ) ,
774+ anchorCellKey : anchorCell . getKey ( ) ,
775+ focusCellKey : focusCell . getKey ( ) ,
776+ } ;
777+ return ;
778+ }
779+
780+ if ( $isRangeSelection ( selection ) ) {
781+ const anchorCell = $getTableCellNodeFromLexicalNode ( selection . anchor . getNode ( ) ) ;
782+ const focusCell = $getTableCellNodeFromLexicalNode ( selection . focus . getNode ( ) ) ;
783+
784+ if ( ! anchorCell || ! focusCell ) {
785+ return ;
786+ }
787+
788+ snapshot = {
789+ type : 'range' ,
790+ anchorKey : selection . anchor . key ,
791+ anchorOffset : selection . anchor . offset ,
792+ anchorType : selection . anchor . type ,
793+ focusKey : selection . focus . key ,
794+ focusOffset : selection . focus . offset ,
795+ focusType : selection . focus . type ,
796+ } ;
797+ }
798+ } ) ;
799+
800+ pendingTableSelectionRef . current = snapshot ;
801+ return snapshot !== null ;
802+ } , [ editor ] ) ;
803+
732804 const applyLink = useCallback (
733805 ( url : string ) => {
734806 closeLinkModal ( ) ;
@@ -814,13 +886,31 @@ const ToolbarPlugin: React.FC<{
814886 ) ;
815887
816888 const closeTableModal = useCallback ( ( ) => {
889+ pendingTableSelectionRef . current = null ;
817890 setIsTableModalOpen ( false ) ;
818891 } , [ ] ) ;
819892
893+ const openTableModal = useCallback ( ( ) => {
894+ if ( readOnly ) {
895+ return ;
896+ }
897+
898+ captureTableSelection ( ) ;
899+ setIsTableModalOpen ( true ) ;
900+ } , [ captureTableSelection , readOnly ] ) ;
901+
820902 const runWithActiveTable = useCallback (
821903 ( action : ( selection : RangeSelection | NodeSelection | TableSelection ) => void ) => {
822904 editor . update ( ( ) => {
823- const selection = $getSelection ( ) ;
905+ let selection = $getSelection ( ) ;
906+ if ( ! selection || ( ! $isRangeSelection ( selection ) && ! $isTableSelection ( selection ) ) ) {
907+ const restoredSelection = restoreSelectionFromSnapshot ( pendingTableSelectionRef . current ) ;
908+ if ( restoredSelection ) {
909+ $setSelection ( restoredSelection ) ;
910+ selection = restoredSelection ;
911+ }
912+ }
913+
824914 if ( ! selection || ( ! $isRangeSelection ( selection ) && ! $isTableSelection ( selection ) ) ) {
825915 return ;
826916 }
@@ -831,7 +921,7 @@ const ToolbarPlugin: React.FC<{
831921 action ( selection ) ;
832922 } ) ;
833923 } ,
834- [ editor ] ,
924+ [ editor , restoreSelectionFromSnapshot ] ,
835925 ) ;
836926
837927 const insertTable = useCallback (
@@ -1090,7 +1180,7 @@ const ToolbarPlugin: React.FC<{
10901180 icon : TableIcon ,
10911181 group : 'insert' ,
10921182 disabled : readOnly ,
1093- onClick : ( ) => setIsTableModalOpen ( true ) ,
1183+ onClick : openTableModal ,
10941184 } ,
10951185 {
10961186 id : 'image' ,
@@ -1183,6 +1273,7 @@ const ToolbarPlugin: React.FC<{
11831273 isStrikethrough ,
11841274 isUnderline ,
11851275 openImagePicker ,
1276+ openTableModal ,
11861277 readOnly ,
11871278 toggleLink ,
11881279 ] ,
0 commit comments