提交 9641703e 编写于 作者: N Nikhil Thorat 提交者: TensorFlower Gardener

Clean up the Bookmarks UI:

-Update the help text to not use "bookmarks" as a noun and a verb
-Change the download icon to the save icon
-Disable the save icon if there are no bookmarks
-Change visible nomenclature to save and load instead of upload/download
-Fix the radio button edge-click bug that selects the first bookmark
-Do some basic checks on the shape of the dataset and the bookmarks.
Change: 137943270
上级 54760a10
......@@ -119,7 +119,7 @@ export class DataSet {
nearestK: number;
tSNEIteration: number = 0;
tSNEShouldStop = true;
dim = [0, 0];
dim: [number, number] = [0, 0];
hasTSNERun: boolean = false;
spriteAndMetadataInfo: SpriteAndMetadataInfo;
......@@ -417,6 +417,9 @@ export class State {
/** The selected projection tab. */
selectedProjection: Projection;
/** Dimensions of the DataSet. */
dataSetDimensions: [number, number];
/** t-SNE parameters */
tSNEIteration: number = 0;
tSNEPerplexity: number = 0;
......
......@@ -127,10 +127,9 @@ paper-textarea {
BOOKMARKS ([[savedStates.length]])
<paper-icon-button icon="help" class="help-icon"></paper-icon-button>
<paper-tooltip animation-delay="0" position="top" offset="0">
The bookmarks drawer allows you to bookmark a set of views into the
projection, saving camera position, selected points, as well as any
highlighting you may have. You can then download the file to save it
permanently, and later upload it to again view your bookmarks.
Open this drawer to save a set of views of the projection, including
selected points. A file containing the bookmarks can then be saved and
later loaded to view them.
</paper-tooltip>
</div>
<div id="icon-container">
......@@ -185,13 +184,14 @@ paper-textarea {
<div id="action-buttons-container">
<paper-icon-button
class="upload-download-icon-button"
icon="file-download"
title="Download bookmarks"
icon="save"
title="Save bookmarks"
disabled="[[!hasStates]]"
on-tap="_downloadFile"></paper-icon-button>
<paper-icon-button
class="upload-download-icon-button"
icon="file-upload"
title="Upload bookmarks"
title="Load bookmarks"
on-tap="_uploadFile"></paper-icon-button>
<paper-icon-button
class="add-icon-button ink-fab"
......
......@@ -14,6 +14,7 @@ limitations under the License.
==============================================================================*/
import {State} from './data';
import {DataProvider, EmbeddingInfo} from './data-provider';
import * as logging from './logging';
import {Projector} from './vz-projector';
// tslint:disable-next-line:no-unused-variable
import {PolymerElement, PolymerHTMLElement} from './vz-projector-util';
......@@ -21,7 +22,13 @@ import {PolymerElement, PolymerHTMLElement} from './vz-projector-util';
// tslint:disable-next-line
export let BookmarkPanelPolymer = PolymerElement({
is: 'vz-projector-bookmark-panel',
properties: {savedStates: Object, selectedState: Number}
properties: {
savedStates: Object,
// Keep a separate polymer property because the savedStates doesn't change
// when adding and removing states.
hasStates: {type: Boolean, value: false},
selectedState: Number
}
});
export class BookmarkPanel extends BookmarkPanelPolymer {
......@@ -30,6 +37,7 @@ export class BookmarkPanel extends BookmarkPanelPolymer {
// A list containing all of the saved states.
private savedStates: State[];
private hasStates = false;
private selectedState: number;
private dom: d3.Selection<any>;
......@@ -85,6 +93,7 @@ export class BookmarkPanel extends BookmarkPanelPolymer {
}
this.push('savedStates', currentState as any);
this.updateHasStates();
}
/** Handles a click on the download bookmarks button. */
......@@ -114,20 +123,28 @@ export class BookmarkPanel extends BookmarkPanelPolymer {
private setupUploadButton() {
// Show and setup the load view button.
let fileInput = this.dom.select('#state-file');
fileInput.on('change', function() {
fileInput.on('change', () => {
let file: File = (d3.event as any).target.files[0];
// Clear out the value of the file chooser. This ensures that if the user
// selects the same file, we'll re-read it.
(d3.event as any).target.value = '';
let fileReader = new FileReader();
fileReader.onload = function(evt) {
fileReader.onload = (evt) => {
let str: string = (evt.target as any).result;
let savedStates = JSON.parse(str);
this.loadAllStates(savedStates);
this.loadSavedState(0);
}.bind(this);
// Verify the bookmarks match.
if (this.savedStatesValid(savedStates)) {
this.loadAllStates(savedStates);
this.loadSavedState(0);
} else {
logging.setWarningMessage(
`Unable to load bookmarks: wrong dataset, expected dataset ` +
`with shape (${savedStates[0].dataSetDimensions}).`);
}
};
fileReader.readAsText(file);
}.bind(this));
});
}
loadAllStates(savedStates: State[]) {
......@@ -135,6 +152,7 @@ export class BookmarkPanel extends BookmarkPanelPolymer {
savedStates[i].isSelected = false;
this.push('savedStates', savedStates[i] as any);
}
this.updateHasStates();
}
/** Deselects any selected state selection. */
......@@ -150,9 +168,7 @@ export class BookmarkPanel extends BookmarkPanelPolymer {
/** Handles a radio button click on a saved state. */
_radioButtonHandler(evt: Event) {
let index =
+(evt.target as Element).parentElement.getAttribute('data-index');
this.loadSavedState(index);
this.loadSavedState(this.getParentDataIndex(evt));
}
loadSavedState(index: number) {
......@@ -174,7 +190,7 @@ export class BookmarkPanel extends BookmarkPanelPolymer {
* Crawls up the DOM to find an ancestor with a data-index attribute. This is
* used to match events to their bookmark index.
*/
_getParentDataIndex(evt: Event) {
private getParentDataIndex(evt: Event) {
for (let i = 0; i < (evt as any).path.length; i++) {
let dataIndex = (evt as any).path[i].getAttribute('data-index');
if (dataIndex != null) {
......@@ -186,13 +202,14 @@ export class BookmarkPanel extends BookmarkPanelPolymer {
/** Handles a clear button click on a bookmark. */
_clearButtonHandler(evt: Event) {
let index = this._getParentDataIndex(evt);
let index = this.getParentDataIndex(evt);
this.splice('savedStates', index, 1);
this.updateHasStates();
}
/** Handles a label change event on a bookmark. */
_labelChange(evt: Event) {
let index = this._getParentDataIndex(evt);
let index = this.getParentDataIndex(evt);
this.savedStates[index].label = (evt.target as any).value;
}
......@@ -219,6 +236,25 @@ export class BookmarkPanel extends BookmarkPanelPolymer {
*/
loadSavedStates(serializedStates: string) {
this.savedStates = JSON.parse(serializedStates);
this.updateHasStates();
}
/**
* Updates the hasState polymer property.
*/
private updateHasStates() {
this.hasStates = (this.savedStates.length !== 0);
}
/** Sanity checks a State array to ensure it matches the current dataset. */
private savedStatesValid(states: State[]): boolean {
for (let i = 0; i < states.length; i++) {
if (states[i].dataSetDimensions[0] !== this.projector.dataSet.dim[0] ||
states[i].dataSetDimensions[1] !== this.projector.dataSet.dim[1]) {
return false;
}
}
return true;
}
}
document.registerElement(BookmarkPanel.prototype.is, BookmarkPanel);
......@@ -494,6 +494,7 @@ export class Projector extends ProjectorPolymer implements SelectionContext,
state.projections.push(projections);
}
state.selectedProjection = this.selectedProjection;
state.dataSetDimensions = this.dataSet.dim;
state.tSNEIteration = this.dataSet.tSNEIteration;
state.selectedPoints = this.selectedPointIndices;
state.cameraDef = this.scatterPlot.getCameraDef();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册