Skip to content

Commit baad620

Browse files
committed
Use SVG for file icons
1 parent bd87296 commit baad620

File tree

11 files changed

+633
-750
lines changed

11 files changed

+633
-750
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
"@jupyterlab/codemirror": "^2.0.0",
5555
"@jupyterlab/console": "^2.0.0",
5656
"@jupyterlab/coreutils": "^4.0.0",
57+
"@jupyterlab/docregistry": "^2.0.0",
5758
"@jupyterlab/filebrowser": "^2.0.0",
5859
"@jupyterlab/mainmenu": "^2.0.0",
5960
"@jupyterlab/nbformat": "^2.0.0",

src/components/FileItem.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ export class FileItem extends React.Component<IFileItemProps> {
106106
)}
107107
<FilePath
108108
filepath={this.props.file.to}
109+
filetype={this.props.file.type}
109110
selected={this.props.selected}
110111
/>
111112
{this.props.actions}

src/components/FilePath.tsx

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import * as React from 'react';
22
import { classes } from 'typestyle';
33
import {
4-
fileIconStyle,
4+
// fileIconStyle,
55
fileLabelStyle,
66
folderLabelStyle
77
} from '../style/FilePathStyle';
8-
import { extractFilename, getFileIconClassName } from '../utils';
8+
import { extractFilename } from '../utils';
9+
import { LabIcon } from '@jupyterlab/ui-components';
10+
import { DocumentRegistry } from '@jupyterlab/docregistry';
911

1012
/**
1113
* FilePath component properties
@@ -15,19 +17,16 @@ export interface IFilePathProps {
1517
* File path
1618
*/
1719
filepath: string;
20+
/**
21+
* File type
22+
*/
23+
filetype: DocumentRegistry.IFileType;
1824
/**
1925
* Is file selected? - impact style of the icon
2026
*/
2127
selected?: boolean;
2228
}
2329

24-
function getFileIconClass(props: IFilePathProps) {
25-
return classes(
26-
fileIconStyle,
27-
getFileIconClassName(props.filepath, props.selected)
28-
);
29-
}
30-
3130
export const FilePath: React.FunctionComponent<IFilePathProps> = (
3231
props: IFilePathProps
3332
) => {
@@ -38,7 +37,12 @@ export const FilePath: React.FunctionComponent<IFilePathProps> = (
3837

3938
return (
4039
<React.Fragment>
41-
<span className={getFileIconClass(props)} />
40+
<LabIcon.resolveReact
41+
icon={props.filetype.icon}
42+
iconClass={classes(props.filetype.iconClass, 'jp-Icon')}
43+
elementPosition="center"
44+
tag="span"
45+
/>
4246
<span className={fileLabelStyle}>
4347
{filename}
4448
<span className={folderLabelStyle}>{folder}</span>

src/components/SinglePastCommitInfo.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ export class SinglePastCommitInfo extends React.Component<
245245
onClick={this._onDiffClickFactory(path, flg)}
246246
title={path}
247247
>
248-
<FilePath filepath={path} />
248+
<FilePath filepath={path} filetype={file.type} />
249249
{flg ? (
250250
<ActionButton iconName="git-diff" title="View file changes" />
251251
) : null}

src/components/diff/DiffWidget.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import { ReactWidget, showDialog } from '@jupyterlab/apputils';
22
import { PathExt } from '@jupyterlab/coreutils';
33
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
44
import * as React from 'react';
5-
import { style } from 'typestyle';
5+
import { GitExtension } from '../../model';
6+
import { diffIcon } from '../../style/icons';
67
import { Diff, isDiffSupported, RenderMimeProvider } from './Diff';
78
import { getRefValue, IDiffContext } from './model';
8-
import { GitExtension } from '../../model';
99

1010
/**
1111
* Method to open a main menu panel to show the diff of a given Notebook file.
@@ -48,9 +48,7 @@ export async function openDiffView(
4848
);
4949
nbDiffWidget.id = id;
5050
nbDiffWidget.title.label = PathExt.basename(filePath);
51-
nbDiffWidget.title.iconClass = style({
52-
backgroundImage: 'var(--jp-icon-diff)'
53-
});
51+
nbDiffWidget.title.icon = diffIcon;
5452
nbDiffWidget.title.closable = true;
5553
nbDiffWidget.addClass('jp-git-diff-parent-diff-widget');
5654

src/model.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { httpGitRequest } from './git';
1010
import { IGitExtension, Git } from './tokens';
1111
import { decodeStage } from './utils';
1212
import { Dialog, showErrorMessage } from '@jupyterlab/apputils';
13+
import { DocumentRegistry } from '@jupyterlab/docregistry';
1314

1415
// Default refresh interval (in milliseconds) for polling the current Git status (NOTE: this value should be the same value as in the plugin settings schema):
1516
const DEFAULT_REFRESH_INTERVAL = 3000; // ms
@@ -635,7 +636,14 @@ export class GitExtension implements IGitExtension {
635636
const data = await response.json();
636637
throw new ServerConnection.ResponseError(response, data.message);
637638
}
638-
return response.json();
639+
640+
// lookup filetypes
641+
const data: Git.ISingleCommitFilePathInfo = await response.json();
642+
data.modified_files = data.modified_files.map(f => {
643+
f.type = this._resolveFileType(f.modified_file_path);
644+
return f;
645+
});
646+
return data;
639647
} catch (err) {
640648
throw new ServerConnection.NetworkError(err);
641649
}
@@ -854,7 +862,11 @@ export class GitExtension implements IGitExtension {
854862

855863
this._setStatus(
856864
(data as Git.IStatusResult).files.map(file => {
857-
return { ...file, status: decodeStage(file.x, file.y) };
865+
return {
866+
...file,
867+
type: this._resolveFileType(file.to),
868+
status: decodeStage(file.x, file.y)
869+
};
858870
})
859871
);
860872
} catch (err) {
@@ -1009,6 +1021,21 @@ export class GitExtension implements IGitExtension {
10091021
}
10101022
}
10111023

1024+
/**
1025+
* Resolve path to filetype
1026+
*/
1027+
protected _resolveFileType(path: string): DocumentRegistry.IFileType {
1028+
// test if directory
1029+
if (path.endsWith('/')) {
1030+
return DocumentRegistry.defaultDirectoryFileType;
1031+
}
1032+
1033+
return (
1034+
this._app.docRegistry.getFileTypesForPath(path)[0] ||
1035+
DocumentRegistry.defaultTextFileType
1036+
);
1037+
}
1038+
10121039
/**
10131040
* Set repository status
10141041
*

src/style/FileItemStyle.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ export const selectedFileStyle = style({
3434
},
3535
'&:hover .jp-icon-selectable[stroke]': {
3636
stroke: 'var(--jp-layout-color2)'
37+
},
38+
'& .jp-icon-selectable[fill]': {
39+
fill: '#fff'
40+
},
41+
'& .jp-icon-selectable-inverse[fill]': {
42+
fill: 'var(--jp-brand-color1)'
3743
}
3844
}
3945
});

src/style/FileListStyle.ts

Lines changed: 0 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -7,83 +7,3 @@ export const fileListWrapperClass = style({
77
overflow: 'hidden',
88
overflowY: 'auto'
99
});
10-
11-
export const notebookFileIconStyle = style({
12-
backgroundImage: 'var(--jp-icon-notebook)'
13-
});
14-
15-
export const folderFileIconStyle = style({
16-
backgroundImage: 'var(--jp-icon-directory)'
17-
});
18-
19-
export const genericFileIconStyle = style({
20-
backgroundImage: 'var(--jp-icon-file)'
21-
});
22-
23-
export const yamlFileIconStyle = style({
24-
backgroundImage: 'var(--jp-icon-yaml)'
25-
});
26-
27-
export const markdownFileIconStyle = style({
28-
backgroundImage: 'var(--jp-icon-markdown)'
29-
});
30-
31-
export const imageFileIconStyle = style({
32-
backgroundImage: 'var(--jp-icon-image)'
33-
});
34-
35-
export const spreadsheetFileIconStyle = style({
36-
backgroundImage: 'var(--jp-icon-spreadsheet)'
37-
});
38-
39-
export const jsonFileIconStyle = style({
40-
backgroundImage: 'var(--jp-icon-json)'
41-
});
42-
43-
export const pythonFileIconStyle = style({
44-
backgroundImage: 'var(--jp-icon-python)'
45-
});
46-
47-
export const kernelFileIconStyle = style({
48-
backgroundImage: 'var(--jp-icon-r)'
49-
});
50-
51-
export const notebookFileIconSelectedStyle = style({
52-
backgroundImage: 'var(--jp-icon-notebook-selected)'
53-
});
54-
55-
export const folderFileIconSelectedStyle = style({
56-
backgroundImage: 'var(--jp-icon-directory-selected)'
57-
});
58-
59-
export const genericFileIconSelectedStyle = style({
60-
backgroundImage: 'var(--jp-icon-file-selected)'
61-
});
62-
63-
export const yamlFileIconSelectedStyle = style({
64-
backgroundImage: 'var(--jp-icon-yaml-selected)'
65-
});
66-
67-
export const markdownFileIconSelectedStyle = style({
68-
backgroundImage: 'var(--jp-icon-markdown-selected)'
69-
});
70-
71-
export const imageFileIconSelectedStyle = style({
72-
backgroundImage: 'var(--jp-icon-image-selected)'
73-
});
74-
75-
export const spreadsheetFileIconSelectedStyle = style({
76-
backgroundImage: 'var(--jp-icon-spreadsheet-selected)'
77-
});
78-
79-
export const jsonFileIconSelectedStyle = style({
80-
backgroundImage: 'var(--jp-icon-json-selected)'
81-
});
82-
83-
export const pythonFileIconSelectedStyle = style({
84-
backgroundImage: 'var(--jp-icon-python-selected)'
85-
});
86-
87-
export const kernelFileIconSelectedStyle = style({
88-
backgroundImage: 'var(--jp-icon-r-selected)'
89-
});

src/tokens.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { IChangedArgs } from '@jupyterlab/coreutils';
2+
import { DocumentRegistry } from '@jupyterlab/docregistry';
23
import { Token, JSONObject } from '@lumino/coreutils';
34
import { IDisposable } from '@lumino/disposable';
45
import { ISignal } from '@lumino/signaling';
@@ -374,6 +375,8 @@ export namespace Git {
374375
to: string;
375376
from: string;
376377
is_binary: boolean | null;
378+
// filetype as determined by app.docRegistry
379+
type?: DocumentRegistry.IFileType;
377380
}
378381

379382
/**
@@ -411,6 +414,8 @@ export namespace Git {
411414
insertion: string;
412415
deletion: string;
413416
is_binary: boolean | null;
417+
// filetype as determined by app.docRegistry
418+
type?: DocumentRegistry.IFileType;
414419
}
415420

416421
/** Interface for GitDetailedLog request result,
@@ -422,15 +427,15 @@ export namespace Git {
422427
modified_files_count?: string;
423428
number_of_insertions?: string;
424429
number_of_deletions?: string;
425-
modified_files?: [ICommitModifiedFile];
430+
modified_files?: ICommitModifiedFile[];
426431
}
427432

428433
/** Interface for GitLog request result,
429434
* has the info of all past commits
430435
*/
431436
export interface ILogResult {
432437
code: number;
433-
commits?: [ISingleCommitInfo];
438+
commits?: ISingleCommitInfo[];
434439
}
435440

436441
export interface IIdentity {

src/utils.ts

Lines changed: 0 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,6 @@
11
import { Dialog, showDialog } from '@jupyterlab/apputils';
22
import { PathExt } from '@jupyterlab/coreutils';
33
import { GitExtension } from './model';
4-
import {
5-
folderFileIconSelectedStyle,
6-
folderFileIconStyle,
7-
genericFileIconSelectedStyle,
8-
genericFileIconStyle,
9-
imageFileIconSelectedStyle,
10-
imageFileIconStyle,
11-
jsonFileIconSelectedStyle,
12-
jsonFileIconStyle,
13-
kernelFileIconSelectedStyle,
14-
kernelFileIconStyle,
15-
markdownFileIconSelectedStyle,
16-
markdownFileIconStyle,
17-
pythonFileIconSelectedStyle,
18-
pythonFileIconStyle,
19-
spreadsheetFileIconSelectedStyle,
20-
spreadsheetFileIconStyle,
21-
yamlFileIconSelectedStyle,
22-
yamlFileIconStyle,
23-
notebookFileIconSelectedStyle,
24-
notebookFileIconStyle
25-
} from './style/FileListStyle';
264
import { Git } from './tokens';
275

286
/** Get the filename from a path */
@@ -78,47 +56,3 @@ export async function openListedFile(
7856
console.error(`Fail to open ${to}.`);
7957
}
8058
}
81-
82-
/**
83-
* Get the extension of a given file
84-
*
85-
* @param path File path for which the icon should be found
86-
* @param selected Is the file selected? Optional: default is false
87-
*/
88-
export function getFileIconClassName(path: string, selected = false): string {
89-
if (path[path.length - 1] === '/') {
90-
return selected ? folderFileIconSelectedStyle : folderFileIconStyle;
91-
}
92-
const fileExtension = PathExt.extname(path).toLocaleLowerCase();
93-
switch (fileExtension) {
94-
case '.md':
95-
return selected ? markdownFileIconSelectedStyle : markdownFileIconStyle;
96-
case '.py':
97-
return selected ? pythonFileIconSelectedStyle : pythonFileIconStyle;
98-
case '.ipynb':
99-
return selected ? notebookFileIconSelectedStyle : notebookFileIconStyle;
100-
case '.json':
101-
return selected ? jsonFileIconSelectedStyle : jsonFileIconStyle;
102-
case '.csv':
103-
case '.xls':
104-
case '.xlsx':
105-
return selected
106-
? spreadsheetFileIconSelectedStyle
107-
: spreadsheetFileIconStyle;
108-
case '.r':
109-
return selected ? kernelFileIconSelectedStyle : kernelFileIconStyle;
110-
case '.yml':
111-
case '.yaml':
112-
return selected ? yamlFileIconSelectedStyle : yamlFileIconStyle;
113-
case '.svg':
114-
case '.tiff':
115-
case '.jpeg':
116-
case '.jpg':
117-
case '.gif':
118-
case '.png':
119-
case '.raw':
120-
return selected ? imageFileIconSelectedStyle : imageFileIconStyle;
121-
default:
122-
return selected ? genericFileIconSelectedStyle : genericFileIconStyle;
123-
}
124-
}

0 commit comments

Comments
 (0)