Skip to content

Commit 33cffea

Browse files
authored
2.2.0 release (#88)
- FIXED: some mountpoints may have no label (Windows) fixes #83 - FIXED: left panel language didn't update on lang change, fixes #80 - FIXED: resizing the window could crop the left panel, fixes #81 - FIXED: borders display bugs, fixes #84 - FIXED: do not show forbidden cursor over the status bar, fixes #86 - UPDATED: theme image un readme file, fixes #85
1 parent c45459a commit 33cffea

File tree

10 files changed

+164
-46
lines changed

10 files changed

+164
-46
lines changed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,10 @@ This will start Jest and run every spec files found in src. Every test file can
102102

103103
### End to End testing
104104

105-
End to end tests are using Cypress and are in the separate `e2e` directory:
105+
End to end tests are using Cypress and are in the separate `e2e` directory. You must install & configure Cypress before running E2E tests. This needs to be done one once using the following commands:
106106

107107
```shell
108-
cd e2e
108+
cd e2e && npm install && cd ..
109109
```
110110

111111
The first time you run the tests, you also need to install cypress dependencies:
@@ -122,6 +122,8 @@ npm run build
122122

123123
This will create a new Electron-Explorer in the `build-e2e` directory.
124124

125+
You may also type `npm run watch` if you want to rebuild automatically the e2e build after a change has been detected inside the sources.
126+
125127
For React-Explorer to run without Electron, a local webserver needs to be started before running the tests:
126128

127129
```shell

e2e/cypress/integration/filetable.spec.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -252,12 +252,19 @@ describe("filetable", () => {
252252
.should("eq", 1);
253253
});
254254

255-
it("arrow up should select the previous element", () => {
256-
// select second element
257-
cy.get("#view_0").trigger("keydown", { keyCode: KEYS.Down });
258-
cy.get("#view_0").trigger("keydown", { keyCode: KEYS.Down });
255+
it.only("arrow up should select the previous element", () => {
256+
// be sure to be in a predictable state, without any selected element
257+
cy.triggerHotkey(`${MOD_KEY}a`);
258+
cy.triggerHotkey(`${MOD_KEY}i`);
259259

260-
// select first element
260+
// activate the first then second element
261+
cy.get("#view_0")
262+
.trigger("keydown", { keyCode: KEYS.Down });
263+
264+
cy.get("#view_0")
265+
.trigger("keydown", { keyCode: KEYS.Down });
266+
267+
// activate previous element: should be the second one
261268
cy.get("#view_0")
262269
.trigger("keydown", { keyCode: KEYS.Up })
263270
.find("[data-cy-file]")

img/react-explorer-theme.png

-28.6 KB
Loading

src/components/App.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { AppState } from "../state/appState";
22
import * as React from "react";
33
import * as ReactDOM from "react-dom";
4+
import { platform } from "process";
45
import { FocusStyleManager, Alert, Classes, Intent } from "@blueprintjs/core";
56
import classNames from "classnames";
67
import { Provider, observer, inject } from "mobx-react";
@@ -351,8 +352,9 @@ class App extends React.Component<AppProps> {
351352
const { t } = this.props;
352353
const winState = this.appState.winStates[0];
353354
const isSplitView = winState.splitView;
354-
const mainClass = classNames('main', {
355-
singleView: !isSplitView
355+
const mainClass = classNames(`main ${platform}`, {
356+
singleView: !isSplitView,
357+
dualView: isSplitView
356358
});
357359
const viewStateLeft = winState.views[0];
358360
const viewStateRight = winState.views[1];

src/components/FileTable.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -791,7 +791,7 @@ export class FileTableClass extends React.Component<IProps, IState> {
791791

792792
onScroll = debounce(({ scrollTop }: any) => {
793793
this.cache.scrollTop = scrollTop;
794-
console.log('onScroll: updating scrollTop', scrollTop, this.cache.path);
794+
// console.log('onScroll: updating scrollTop', scrollTop, this.cache.path);
795795
}, SCROLL_DEBOUNCE);
796796

797797
rowGetter = (index: Index) => this.getRow(index.index);
@@ -801,7 +801,7 @@ export class FileTableClass extends React.Component<IProps, IState> {
801801
const { position } = this.state;
802802
const rowCount = this.state.nodes.length;
803803

804-
return (<div ref={this.setTableRef} onKeyDown={this.onInputKeyDown} className={`fileListSizerWrapper ${Classes.ELEVATION_0}`}>
804+
return (<div ref={this.setTableRef} onKeyDown={this.onInputKeyDown} className={`fileListSizerWrapper`}>
805805
<AutoSizer>
806806
{({ width, height }) => (
807807
<Table

src/components/LeftPanel.tsx

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { observer, inject } from "mobx-react";
44
import { withNamespaces, WithNamespaces } from 'react-i18next';
55
import classNames from "classnames";
66
import { IReactionDisposer, reaction, toJS } from "mobx";
7+
import i18next from 'i18next';
78
import { USERNAME } from "../utils/platform";
89
import Icons from "../constants/icons";
910
import { FavoritesState, Favorite } from "../state/favoritesState";
@@ -58,14 +59,30 @@ export class LeftPanelClass extends React.Component<IProps, LeftPanelState> {
5859
this.favoritesState = this.injected.appState.favoritesState;
5960

6061
this.installReaction();
62+
this.bindLanguageChange();
6163
}
6264

65+
private bindLanguageChange = () => {
66+
console.log('languageChanged');
67+
i18next.on('languageChanged', this.onLanguageChanged);
68+
}
69+
70+
private unbindLanguageChange = () => {
71+
i18next.off('languageChanged', this.onLanguageChanged);
72+
}
73+
74+
public onLanguageChanged = (lang: string) => {
75+
console.log('building nodes', lang);
76+
this.buildNodes(this.favoritesState);
77+
}
78+
6379
private get injected() {
6480
return this.props as InjectedProps;
6581
}
6682

6783
componentWillUnmount() {
6884
this.disposers.forEach(disposer => disposer());
85+
this.unbindLanguageChange();
6986
}
7087

7188
private installReaction() {
@@ -152,21 +169,25 @@ export class LeftPanelClass extends React.Component<IProps, LeftPanelState> {
152169
shortcuts.childNodes = favorites.shortcuts.map((shortcut, i) => ({
153170
id: `s_${shortcut.path}`,
154171
key: `s_${shortcut.path}`,
155-
label: shortcut.label === 'HOME_DIR' ? USERNAME: t(`FAVORITES_PANEL.${shortcut.label}`),
172+
label: <span title={shortcut.path}>
173+
{shortcut.label === 'HOME_DIR' ? USERNAME : t(`FAVORITES_PANEL.${shortcut.label}`)}
174+
</span>,
156175
icon: Icons[shortcut.label],
157-
title: shortcut.path,
158176
nodeData: shortcut.path
159177
}));
160178

161179
places.childNodes = favorites.places.map((place, i) => ({
162180
id: `p_${place.path}`,
163181
key: `p_${place.path}`,
164-
label: place.label,
182+
label: <span title={place.path}>{place.label}</span>,
165183
icon: place.icon,
166-
title: place.path,
167184
nodeData: place.path
168185
}));
169186

187+
// update root nodes label too
188+
places.label = t('FAVORITES_PANEL.PLACES');
189+
shortcuts.label = t('FAVORITES_PANEL.SHORTCUTS');
190+
170191
this.setState(this.state);
171192
}
172193

src/components/SideView.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ export class SideViewClass extends React.Component<InjectedProps>{
9292

9393
let activeClass = classnames('sideview', {
9494
active: active,
95+
inactive: !active,
9596
hidden: this.props.hide,
9697
dropTarget: dropAndOver,
9798
notDropTarget: isOver && !canDrop

src/css/favoritesPanel.css

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,27 @@
33
overflow:auto;
44
flex-shrink:0;
55
width:200px;
6-
border:1px dotted gray;
6+
/* border:1px dotted gray; */
77
margin: 0;
88
list-style-type: none;
99
background: rgba(206, 217, 224, 0.5);
10-
padding: 5px;
10+
padding: 0;
1111
font-size:13px;
1212
}
1313

14-
.favoritesPanel:before {
14+
.bp3-dark .favoritesPanel {
15+
background-color:rgb(41, 56, 66);
16+
}
17+
18+
/* .favoritesPanel:before {
1519
position: absolute;
1620
top: -2px;
1721
right: -2px;
1822
bottom: -2px;
1923
left: -2px;
2024
border-radius: 4px;
2125
content: "";
22-
box-shadow: 0 0 2px 2px inset rgb(231, 236, 239);
23-
}
26+
} */
2427

2528
.favoritesPanel .bp3-tree-node-content-0 {
2629
color:#5c7080;

src/css/main.css

Lines changed: 106 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ button.small:hover{
4343
-webkit-user-select: none;
4444
}
4545

46+
.sideview > .bp3-control-group > :first-child,
47+
.sideview > .bp3-control-group > :last-child {
48+
border-radius:0;
49+
}
50+
4651
.main > .hidden{
4752
display:none;
4853
pointer-events: none;
@@ -66,56 +71,89 @@ button.small:hover{
6671
justify-content: center;
6772
flex-grow:1;
6873
overflow:hidden;
69-
padding:10px;
74+
/* padding:10px; */
7075
}
7176

72-
#root .sideview{
77+
#root .sideview {
7378
position:relative;
74-
width:50%;
75-
min-width:360px;
79+
width:calc((100% - 200px) / 2);
7680
flex-grow:1;
7781
}
7882

79-
#root .sideview:before{
83+
#root .singleView .sideview {
84+
width:calc(100% - 200px);
85+
}
86+
87+
#root .sideview {
88+
border-left: 1px solid rgb(189, 195, 199);
89+
}
90+
91+
#root .dualView #view_0.inactive {
92+
border-right-color:transparent;
93+
}
94+
95+
#root .dualView #view_0.active {
96+
border-left-color:transparent;
97+
}
98+
99+
#root .dualView #view_1.inactive{
100+
border-left-color: rgb(131, 135, 138);
101+
}
102+
103+
#root .darwin.dualView #view_1.active:before {
104+
border-bottom-right-radius: 5px;
105+
}
106+
107+
#root .dualView .sideview:before{
80108
position: absolute;
81-
top: -2px;
82-
right: -2px;
83-
bottom: -2px;
84-
left: -2px;
85-
border-radius: 4px;
109+
pointer-events:none;
110+
top: 0;
111+
right: 0;
112+
bottom: 0;
113+
left: -1px;
86114
content: "";
115+
opacity: .6;
116+
z-index: 9;
117+
border: 1px solid rgb(189, 195, 199);
118+
border-top:none;
119+
border-bottom:none;
87120
}
88121

89-
#root .sideview:before{
90-
box-shadow: 0 0 2px 2px inset rgb(231, 236, 239);
122+
#root .dualView .sideview.active:before {
123+
opacity:1;
124+
border: 2px solid rgba(19, 124, 189, .9);
91125
}
92126

93-
.bp3-dark #root .sideview:before{
94-
box-shadow: 0 0 2px 2px inset rgb(66, 84, 97);
127+
#root .dualView .sideview.inactive:before{
128+
border-left:none;
95129
}
96130

97-
#root .sideview.active:before{
98-
box-shadow: 0 0 0px 2px inset rgba(19, 124, 189, 0.8);
99-
z-index:2;
100-
pointer-events: none;
131+
/* dark theme */
132+
.bp3-dark #root .singleView .sideview {
133+
border-left: 1px solid rgb(14,25,34);
101134
}
102135

103-
#root .singleView .sideview.active:before{
104-
box-shadow: 0 0 2px 2px inset rgb(219, 219, 219);
136+
.bp3-dark #root .sideview,
137+
.bp3-dark #root .dualView #view_1.inactive {
138+
border-color: rgb(14,25,34);
105139
}
106140

107-
.bp3-dark #root .singleView .sideview:before{
108-
box-shadow: 0 0 2px 2px inset rgb(66, 84, 97);
141+
.bp3-dark #root .dualView .sideview.inactive:before {
142+
border-color: rgb(73, 75, 76);
109143
}
110144

111-
#root .sideview.dropTarget:before{
112-
box-shadow: 0 0 4px 2px inset #00b63d82;
145+
#root .sideview.dropTarget:before {
146+
box-shadow: 0 0 4px 2px inset #00b63ddb;
113147
}
114148

115149
#root .sideview.notDropTarget{
116150
cursor:no-drop;
117151
}
118152

153+
#root .sideview.notDropTarget:before {
154+
box-shadow: 0 0 4px 2px inset #b6000982;
155+
}
156+
119157
#root .sideview:first-child{
120158
margin-right:10px;
121159
}
@@ -180,6 +218,19 @@ button.small:hover{
180218
vertical-align:top;
181219
}
182220

221+
#root .bp3-navbar {
222+
border-bottom: 1px solid rgb(189, 195, 199);
223+
background: rgb(246,246,246);
224+
background: linear-gradient(180deg, rgba(246,246,246,1) 16%, rgba(230,236,239,1) 100%);
225+
box-shadow:none;
226+
}
227+
228+
.bp3-dark #root .bp3-navbar {
229+
border-bottom: 1px solid rgb(14,25,34);
230+
background: rgb(46,46,46);
231+
background: linear-gradient(180deg, rgba(56,56,56,1) 16%, rgba(40,56,69,1) 100%);
232+
}
233+
183234
.bp3-navbar .download .bp3-button-text{
184235
display: flex;
185236
align-items: center;
@@ -300,6 +351,24 @@ body.bp3-dark .bp3-loader.active{
300351
border:1px solid rgba(16,22,26,.2);
301352
}
302353

354+
.status-bar {
355+
border-top: 1px solid rgb(189, 195, 199);
356+
}
357+
358+
/* FIXME: we only used disable state to use the styles associated with disabled state */
359+
.status-bar .bp3-input:disabled,
360+
.status-bar .bp3-input.bp3-disabled,
361+
.status-bar .bp3-button.bp3-disabled,
362+
.status-bar button.bp3-minimal.bp3-disabled,
363+
.status-bar button.bp3-minimal.bp3-disabled:hover,
364+
.status-bar.bp3-input-group.bp3-disabled {
365+
cursor:default;
366+
}
367+
368+
.bp3-dark .status-bar {
369+
border-top: 1px solid rgb(14,25,34);
370+
}
371+
303372
.status-bar.offline > .bp3-icon:after{
304373
border-bottom: 1px solid rgba(255,0,0,.6);
305374
content: "";
@@ -373,9 +442,21 @@ body.bp3-dark .bp3-loader.active{
373442
background-color: rgb(88, 100, 109);
374443
}
375444

445+
.sideview > .toolbar > .bp3-button-group > .bp3-button:not([class*="bp3-intent-"]) {
446+
box-shadow: inset 0 0 0 1px rgba(16, 22, 26, 0.2), inset 0 -1px 0 rgba(16, 22, 26, 0.1);
447+
}
448+
449+
.sideview > .toolbar > .bp3-button-group > .bp3-button:first-child:not([class*="bp3-intent-"]) {
450+
margin-left:-1px;
451+
}
452+
453+
.sideview > .toolbar > .bp3-button-group > .bp3-button:last-child:not([class*="bp3-intent-"]) {
454+
box-shadow:inset 0 -1px 0 rgba(16, 22, 26, 0.1);
455+
}
456+
376457
.tablist .bp3-button .bp3-button-text{
377458
overflow:hidden;
378-
white-space: nowrap;
459+
white-space: nowrap;
379460
text-overflow:ellipsis;
380461
}
381462

src/state/favoritesState.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ export class FavoritesState {
3838
const mountpoint = drive.mountpoints[0];
3939

4040
return {
41-
label: mountpoint.label,
41+
// Some mountpoints may not have a label (eg. win: c:\)
42+
label: mountpoint.label || mountpoint.path,
4243
path: mountpoint.path,
4344
icon: (drive.isRemovable || drive.isVirtual) ? IconNames.FLOPPY_DISK : IconNames.DATABASE
4445
} as Favorite;

0 commit comments

Comments
 (0)