3
3
* License: MS-RSL – see LICENSE.md for details
4
4
*/
5
5
6
- import { Alert } from "antd" ;
6
+ import { Alert , Radio , Space , Tooltip } from "antd" ;
7
7
import * as immutable from "immutable" ;
8
+ import * as _ from "lodash" ;
8
9
import React from "react" ;
9
10
import { FormattedMessage } from "react-intl" ;
10
- import * as underscore from "underscore" ;
11
11
12
12
import { UsersViewing } from "@cocalc/frontend/account/avatar/users-viewing" ;
13
13
import { Button , ButtonGroup , Col , Row } from "@cocalc/frontend/antd-bootstrap" ;
14
14
import {
15
- TypedMap ,
16
15
project_redux_name ,
17
16
rclass ,
18
17
redux ,
19
18
rtypes ,
19
+ TypedMap ,
20
20
} from "@cocalc/frontend/app-framework" ;
21
21
import { ShallowTypedMap } from "@cocalc/frontend/app-framework/ShallowTypedMap" ;
22
22
import {
@@ -41,14 +41,16 @@ import {
41
41
} from "@cocalc/frontend/project_configuration" ;
42
42
import { ProjectActions } from "@cocalc/frontend/project_store" ;
43
43
import { ProjectMap , ProjectStatus } from "@cocalc/frontend/todo-types" ;
44
+ import { unreachable } from "@cocalc/util/misc" ;
44
45
import AskNewFilename from "../ask-filename" ;
45
46
import { useProjectContext } from "../context" ;
46
47
import { AccessErrors } from "./access-errors" ;
47
48
import { ActionBar } from "./action-bar" ;
48
49
import { ActionBox } from "./action-box" ;
49
50
import { FetchDirectoryErrors } from "./fetch-directory-errors" ;
50
51
import { FileListing } from "./file-listing" ;
51
- import { default_ext } from "./file-listing/utils" ;
52
+ import { TerminalModeDisplay } from "./file-listing/terminal-mode-display" ;
53
+ import { default_ext , TERM_MODE_CHAR } from "./file-listing/utils" ;
52
54
import { MiniTerminal } from "./mini-terminal" ;
53
55
import { MiscSideButtons } from "./misc-side-buttons" ;
54
56
import { NewButton } from "./new-button" ;
@@ -148,11 +150,10 @@ export function Explorer() {
148
150
const Explorer0 = rclass (
149
151
class Explorer extends React . Component < ReactProps & ReduxProps , State > {
150
152
newFileRef = React . createRef < any > ( ) ;
151
- searchBarRef = React . createRef < any > ( ) ;
153
+ searchAndTerminalBar = React . createRef < any > ( ) ;
152
154
fileListingRef = React . createRef < any > ( ) ;
153
155
currentDirectoryRef = React . createRef < any > ( ) ;
154
156
miscButtonsRef = React . createRef < any > ( ) ;
155
- miniterminalRef = React . createRef < any > ( ) ;
156
157
157
158
static reduxProps = ( { name } ) => {
158
159
return {
@@ -412,7 +413,7 @@ const Explorer0 = rclass(
412
413
return (
413
414
< ActivityDisplay
414
415
trunc = { 80 }
415
- activity = { underscore . values ( this . props . activity ) }
416
+ activity = { _ . values ( this . props . activity ) }
416
417
on_clear = { ( ) => this . props . actions . clear_all_activity ( ) }
417
418
style = { { top : "100px" } }
418
419
/>
@@ -565,10 +566,17 @@ const Explorer0 = rclass(
565
566
flexFlow : IS_MOBILE ? undefined : "row wrap" ,
566
567
justifyContent : "space-between" ,
567
568
alignItems : "stretch" ,
569
+ marginBottom : "15px" ,
568
570
} }
569
571
>
570
- < div >
571
- < div style = { { display : "flex" } } >
572
+ < div
573
+ style = { {
574
+ flex : "3 1 auto" ,
575
+ display : "flex" ,
576
+ flexDirection : "column" ,
577
+ } }
578
+ >
579
+ < div style = { { display : "flex" , flex : "1 1 auto" } } >
572
580
< SelectComputeServerForFileExplorer
573
581
project_id = { this . props . project_id }
574
582
key = "compute-server"
@@ -603,67 +611,32 @@ const Explorer0 = rclass(
603
611
style = { {
604
612
flex : "0 1 auto" ,
605
613
margin : "0 10px" ,
606
- marginBottom : "15px" ,
607
614
} }
608
615
className = "cc-project-files-create-dropdown"
609
616
>
610
617
{ this . render_new_file ( ) }
611
618
</ div >
612
619
) }
613
620
{ ! IS_MOBILE && (
614
- < div
615
- ref = { this . searchBarRef }
616
- style = { {
617
- flex : "1 0 20%" ,
618
- minWidth : "15em" ,
619
- } }
620
- >
621
- < SearchBar
622
- project_id = { this . props . project_id }
623
- key = { this . props . current_path }
624
- file_search = { this . props . file_search }
625
- actions = { this . props . actions }
626
- current_path = { this . props . current_path }
627
- selected_file = {
628
- visible_listing != undefined
629
- ? visible_listing [ this . props . selected_file_index || 0 ]
630
- : undefined
631
- }
632
- selected_file_index = { this . props . selected_file_index }
633
- file_creation_error = { this . props . file_creation_error }
634
- num_files_displayed = {
635
- visible_listing != undefined
636
- ? visible_listing . length
637
- : undefined
638
- }
639
- create_file = { this . create_file }
640
- create_folder = { this . create_folder }
641
- />
642
- </ div >
621
+ < SearchTerminalBar
622
+ ref = { this . searchAndTerminalBar }
623
+ actions = { this . props . actions }
624
+ current_path = { this . props . current_path }
625
+ file_search = { this . props . file_search }
626
+ visible_listing = { visible_listing }
627
+ selected_file_index = { this . props . selected_file_index }
628
+ file_creation_error = { this . props . file_creation_error }
629
+ create_file = { this . create_file }
630
+ create_folder = { this . create_folder }
631
+ />
643
632
) }
644
- < >
645
- < div
646
- style = { {
647
- flex : "0 1 auto" ,
648
- marginBottom : "15px" ,
649
- } }
650
- >
651
- < UsersViewing project_id = { this . props . project_id } />
652
- </ div >
653
- { ! IS_MOBILE && (
654
- < div
655
- ref = { this . miniterminalRef }
656
- style = { { flex : "1 0 auto" , marginBottom : "15px" } }
657
- >
658
- < MiniTerminal
659
- current_path = { this . props . current_path }
660
- project_id = { this . props . project_id }
661
- actions = { this . props . actions }
662
- show_close_x = { true }
663
- />
664
- </ div >
665
- ) }
666
- </ >
633
+ < div
634
+ style = { {
635
+ flex : "0 1 auto" ,
636
+ } }
637
+ >
638
+ < UsersViewing project_id = { this . props . project_id } />
639
+ </ div >
667
640
</ div >
668
641
) ;
669
642
}
@@ -714,6 +687,11 @@ const Explorer0 = rclass(
714
687
/>
715
688
) ;
716
689
}
690
+ render_terminal_mode ( ) {
691
+ if ( this . props . file_search [ 0 ] === TERM_MODE_CHAR ) {
692
+ return < TerminalModeDisplay /> ;
693
+ }
694
+ }
717
695
718
696
render ( ) {
719
697
let project_is_running : boolean ,
@@ -763,7 +741,7 @@ const Explorer0 = rclass(
763
741
alignItems : "stretch" ,
764
742
} ;
765
743
766
- // be careful with adding height:'100%'. it could cause flex to miscalc . see #3904
744
+ // be careful with adding height:'100%'. it could cause flex to miscalculate . see #3904
767
745
return (
768
746
< div className = { "smc-vfill" } >
769
747
< div
@@ -777,6 +755,7 @@ const Explorer0 = rclass(
777
755
{ this . render_error ( ) }
778
756
{ this . render_activity ( ) }
779
757
{ this . render_control_row ( visible_listing ) }
758
+ { this . render_terminal_mode ( ) }
780
759
{ this . props . ext_selection != null && (
781
760
< AskNewFilename project_id = { this . props . project_id } />
782
761
) }
@@ -806,6 +785,7 @@ const Explorer0 = rclass(
806
785
< Row > { this . render_files_action_box ( file_map ) } </ Row >
807
786
) : undefined }
808
787
</ div >
788
+
809
789
< div
810
790
ref = { this . fileListingRef }
811
791
className = "smc-vfill"
@@ -832,14 +812,108 @@ const Explorer0 = rclass(
832
812
open = { this . props . explorerTour }
833
813
project_id = { this . props . project_id }
834
814
newFileRef = { this . newFileRef }
835
- searchBarRef = { this . searchBarRef }
815
+ searchAndTerminalBar = { this . searchAndTerminalBar }
836
816
fileListingRef = { this . fileListingRef }
837
817
currentDirectoryRef = { this . currentDirectoryRef }
838
818
miscButtonsRef = { this . miscButtonsRef }
839
- miniterminalRef = { this . miniterminalRef }
840
819
/>
841
820
</ div >
842
821
) ;
843
822
}
844
823
} ,
845
824
) ;
825
+
826
+ const SearchTerminalBar = React . forwardRef (
827
+ (
828
+ {
829
+ current_path,
830
+ file_search,
831
+ actions,
832
+ visible_listing,
833
+ selected_file_index,
834
+ file_creation_error,
835
+ create_file,
836
+ create_folder,
837
+ } : {
838
+ ref : React . Ref < any > ;
839
+ current_path : string ;
840
+ file_search : string ;
841
+ actions : ProjectActions ;
842
+ visible_listing : ListingItem [ ] | undefined ;
843
+ selected_file_index ?: number ;
844
+ file_creation_error ?: string ;
845
+ create_file : ( ext ?: string , switch_over ?: boolean ) => void ;
846
+ create_folder : ( switch_over ?: boolean ) => void ;
847
+ } ,
848
+ ref : React . LegacyRef < HTMLDivElement > | undefined ,
849
+ ) => {
850
+ const [ mode , setMode ] = React . useState < "search" | "terminal" > ( "search" ) ;
851
+
852
+ function renderTerminal ( ) {
853
+ return (
854
+ < MiniTerminal
855
+ current_path = { current_path }
856
+ actions = { actions }
857
+ show_close_x = { true }
858
+ />
859
+ ) ;
860
+ }
861
+
862
+ function renderSearch ( ) {
863
+ return (
864
+ < SearchBar
865
+ key = { current_path }
866
+ file_search = { file_search }
867
+ actions = { actions }
868
+ current_path = { current_path }
869
+ selected_file = {
870
+ visible_listing != undefined
871
+ ? visible_listing [ selected_file_index || 0 ]
872
+ : undefined
873
+ }
874
+ selected_file_index = { selected_file_index }
875
+ file_creation_error = { file_creation_error }
876
+ num_files_displayed = {
877
+ visible_listing != undefined ? visible_listing . length : undefined
878
+ }
879
+ create_file = { create_file }
880
+ create_folder = { create_folder }
881
+ />
882
+ ) ;
883
+ }
884
+
885
+ function renderBar ( ) {
886
+ switch ( mode ) {
887
+ case "search" :
888
+ return renderSearch ( ) ;
889
+ case "terminal" :
890
+ return renderTerminal ( ) ;
891
+ default :
892
+ unreachable ( mode ) ;
893
+ }
894
+ }
895
+
896
+ return (
897
+ < Space . Compact style = { { flex : "1 1 auto" } } >
898
+ < Radio . Group
899
+ ref = { ref }
900
+ value = { mode }
901
+ onChange = { ( e ) => setMode ( e . target . value ) }
902
+ style = { { whiteSpace : "nowrap" } }
903
+ >
904
+ < Tooltip title = "Click to change the search box to filter files by their name" >
905
+ < Radio . Button value = "search" >
906
+ < Icon name = "search" />
907
+ </ Radio . Button >
908
+ </ Tooltip >
909
+ < Tooltip title = "Click to change the search box to run commands in the terminal" >
910
+ < Radio . Button value = "terminal" style = { { borderRadius : 0 } } >
911
+ < Icon name = "terminal" />
912
+ </ Radio . Button >
913
+ </ Tooltip >
914
+ </ Radio . Group >
915
+ { renderBar ( ) }
916
+ </ Space . Compact >
917
+ ) ;
918
+ } ,
919
+ ) ;
0 commit comments