style: enforce LF linebreak and fix existing files (#15)
This commit is contained in:
Родитель
b11e08304e
Коммит
ade7902b47
|
@ -50,7 +50,8 @@
|
|||
"react-start": "react-scripts start",
|
||||
"test": "react-scripts test --env=jsdom --silent",
|
||||
"eject": "react-scripts eject",
|
||||
"tslint": "./node_modules/.bin/tslint 'src/**/*.ts*'"
|
||||
"tslint": "./node_modules/.bin/tslint 'src/**/*.ts*'",
|
||||
"tslintfix": "./node_modules/.bin/tslint 'src/**/*.ts*' --fix"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
|
|
|
@ -1,37 +1,37 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import React from "react";
|
||||
import App from "./App";
|
||||
import { Provider } from "react-redux";
|
||||
import createReduxStore from "./redux/store/store";
|
||||
import initialState from "./redux/store/initialState";
|
||||
import { IApplicationState } from "./models//applicationState";
|
||||
import { mount } from "enzyme";
|
||||
import { Router } from "react-router-dom";
|
||||
import { KeyboardManager } from "./react/components/common/keyboardManager/keyboardManager";
|
||||
import { ErrorHandler } from "./react/components/common/errorHandler/errorHandler";
|
||||
|
||||
describe("App Component", () => {
|
||||
const defaultState: IApplicationState = initialState;
|
||||
const store = createReduxStore(defaultState);
|
||||
|
||||
function createComponent() {
|
||||
return mount(
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>,
|
||||
);
|
||||
}
|
||||
|
||||
it("renders without crashing", () => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it("renders required top level components", () => {
|
||||
const wrapper = createComponent();
|
||||
expect(wrapper.find(Router).exists()).toBe(true);
|
||||
expect(wrapper.find(KeyboardManager).exists()).toEqual(true);
|
||||
expect(wrapper.find(ErrorHandler).exists()).toEqual(true);
|
||||
});
|
||||
});
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import React from "react";
|
||||
import App from "./App";
|
||||
import { Provider } from "react-redux";
|
||||
import createReduxStore from "./redux/store/store";
|
||||
import initialState from "./redux/store/initialState";
|
||||
import { IApplicationState } from "./models//applicationState";
|
||||
import { mount } from "enzyme";
|
||||
import { Router } from "react-router-dom";
|
||||
import { KeyboardManager } from "./react/components/common/keyboardManager/keyboardManager";
|
||||
import { ErrorHandler } from "./react/components/common/errorHandler/errorHandler";
|
||||
|
||||
describe("App Component", () => {
|
||||
const defaultState: IApplicationState = initialState;
|
||||
const store = createReduxStore(defaultState);
|
||||
|
||||
function createComponent() {
|
||||
return mount(
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>,
|
||||
);
|
||||
}
|
||||
|
||||
it("renders without crashing", () => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it("renders required top level components", () => {
|
||||
const wrapper = createComponent();
|
||||
expect(wrapper.find(Router).exists()).toBe(true);
|
||||
expect(wrapper.find(KeyboardManager).exists()).toEqual(true);
|
||||
expect(wrapper.find(ErrorHandler).exists()).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
* Constants used throughout application
|
||||
*/
|
||||
export const constants = {
|
||||
version: "pubpreview_1.0",
|
||||
projectFormTempKey: "projectForm",
|
||||
projectFileExtension: ".vott",
|
||||
labelFileExtension: ".labels.json",
|
||||
ocrFileExtension: ".ocr.json",
|
||||
fieldsFileName: "fields.json",
|
||||
maxConcurrentServiceRequests: 3,
|
||||
statusCodeSucceeded: "succeeded",
|
||||
statusCodeFailed: "failed",
|
||||
apiKeyHeader: "Ocp-Apim-Subscription-Key",
|
||||
maxRetry: 8,
|
||||
initialRetryInterval: 500, // ms
|
||||
convertedImageFormat: "image/jpeg",
|
||||
convertedImageQuality: 0.7,
|
||||
convertedThumbnailQuality: 0.2,
|
||||
|
||||
apiModelsPath: "/formrecognizer/v2.0-preview/custom/models",
|
||||
};
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
* Constants used throughout application
|
||||
*/
|
||||
export const constants = {
|
||||
version: "pubpreview_1.0",
|
||||
projectFormTempKey: "projectForm",
|
||||
projectFileExtension: ".vott",
|
||||
labelFileExtension: ".labels.json",
|
||||
ocrFileExtension: ".ocr.json",
|
||||
fieldsFileName: "fields.json",
|
||||
maxConcurrentServiceRequests: 3,
|
||||
statusCodeSucceeded: "succeeded",
|
||||
statusCodeFailed: "failed",
|
||||
apiKeyHeader: "Ocp-Apim-Subscription-Key",
|
||||
maxRetry: 8,
|
||||
initialRetryInterval: 500, // ms
|
||||
convertedImageFormat: "image/jpeg",
|
||||
convertedImageQuality: 0.7,
|
||||
convertedThumbnailQuality: 0.2,
|
||||
|
||||
apiModelsPath: "/formrecognizer/v2.0-preview/custom/models",
|
||||
};
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,11 +1,11 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
export class AzureBlobStorageError extends Error {
|
||||
public statusCode: number;
|
||||
|
||||
constructor(statusCode: number) {
|
||||
super();
|
||||
this.statusCode = statusCode;
|
||||
}
|
||||
}
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
export class AzureBlobStorageError extends Error {
|
||||
public statusCode: number;
|
||||
|
||||
constructor(statusCode: number) {
|
||||
super();
|
||||
this.statusCode = statusCode;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
/// <reference types="react-scripts" />
|
||||
/// <reference types="react-scripts" />
|
||||
|
|
|
@ -1,313 +1,313 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { Feature, MapBrowserEvent, View } from "ol";
|
||||
import { Extent, getCenter } from "ol/extent";
|
||||
import { defaults as defaultInteractions, DragPan, DragRotateAndZoom, Interaction } from "ol/interaction.js";
|
||||
import ImageLayer from "ol/layer/Image";
|
||||
import Layer from "ol/layer/Layer";
|
||||
import VectorLayer from "ol/layer/Vector";
|
||||
import Map from "ol/Map";
|
||||
import Projection from "ol/proj/Projection";
|
||||
import Static from "ol/source/ImageStatic.js";
|
||||
import VectorSource from "ol/source/Vector";
|
||||
import * as React from "react";
|
||||
import "./styles.css";
|
||||
import Utils from "./utils";
|
||||
|
||||
interface IImageMapProps {
|
||||
imageUri: string;
|
||||
imageWidth: number;
|
||||
imageHeight: number;
|
||||
imageAngle?: number;
|
||||
|
||||
featureStyler?: any;
|
||||
|
||||
enableFeatureSelection?: boolean;
|
||||
handleFeatureSelect?: (feature: any, isTaggle: boolean) => void;
|
||||
|
||||
onMapReady: () => void;
|
||||
}
|
||||
|
||||
export class ImageMap extends React.Component<IImageMapProps> {
|
||||
private map: Map;
|
||||
private imageLayer: ImageLayer;
|
||||
private vectorLayer: VectorLayer;
|
||||
|
||||
private mapElement: HTMLDivElement | null = null;
|
||||
|
||||
private imageExtent: number[];
|
||||
|
||||
private countPointerDown: number = 0;
|
||||
private isSwiping: boolean = false;
|
||||
|
||||
private readonly VECTOR_LAYER_NAME = "vectorLayer";
|
||||
|
||||
private ignorePointerMoveEventCount: number = 5;
|
||||
private pointerMoveEventCount: number = 0;
|
||||
|
||||
private vectorLayerFilter = {
|
||||
layerFilter: (layer: Layer) => layer.get("name") === this.VECTOR_LAYER_NAME,
|
||||
};
|
||||
|
||||
constructor(props: IImageMapProps) {
|
||||
super(props);
|
||||
|
||||
this.imageExtent = [0, 0, this.props.imageWidth, this.props.imageHeight];
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
this.initMap();
|
||||
}
|
||||
|
||||
public componentDidUpdate(prevProps: IImageMapProps) {
|
||||
if (prevProps.imageUri !== this.props.imageUri) {
|
||||
this.imageExtent = [0, 0, this.props.imageWidth, this.props.imageHeight];
|
||||
this.setImage(this.props.imageUri, this.imageExtent);
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<div className="map-wrapper">
|
||||
<div id="map" className="map" ref={(el) => this.mapElement = el}></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one feature to the map
|
||||
*/
|
||||
public addFeature = (feature: Feature) => {
|
||||
this.vectorLayer.getSource().addFeature(feature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add features to the map
|
||||
*/
|
||||
public addFeatures = (features: Feature[]) => {
|
||||
this.vectorLayer.getSource().addFeatures(features);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add interaction to the map
|
||||
*/
|
||||
public addInteraction = (interaction: Interaction) => {
|
||||
this.map.addInteraction(interaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all features from the map
|
||||
*/
|
||||
public getAllFeatures = () => {
|
||||
return this.vectorLayer.getSource().getFeatures();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove specific feature object from the map
|
||||
*/
|
||||
public removeFeature = (feature: Feature) => {
|
||||
this.vectorLayer.getSource().removeFeature(feature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all features from the map
|
||||
*/
|
||||
public removeAllFeatures = () => {
|
||||
this.vectorLayer.getSource().clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove interaction from the map
|
||||
*/
|
||||
public removeInteraction = (interaction: Interaction) => {
|
||||
this.map.removeInteraction(interaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the image extent (left, top, right, bottom)
|
||||
*/
|
||||
public getImageExtent = () => {
|
||||
return this.imageExtent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get features at specific extend
|
||||
*/
|
||||
public getFeaturesInExtent = (extent: Extent): Feature[] => {
|
||||
const features: Feature[] = [];
|
||||
this.vectorLayer.getSource().forEachFeatureInExtent(extent, (feature) => {
|
||||
features.push(feature);
|
||||
});
|
||||
return features;
|
||||
}
|
||||
|
||||
private initMap = () => {
|
||||
const projection = this.createProjection(this.imageExtent);
|
||||
|
||||
this.imageLayer = new ImageLayer({
|
||||
source: this.createImageSource(this.props.imageUri, projection, this.imageExtent),
|
||||
});
|
||||
|
||||
const options: any = {};
|
||||
options.name = this.VECTOR_LAYER_NAME;
|
||||
options.style = this.props.featureStyler;
|
||||
options.source = new VectorSource();
|
||||
this.vectorLayer = new VectorLayer(options);
|
||||
|
||||
this.map = new Map({
|
||||
interactions: defaultInteractions({ doubleClickZoom: false }).extend([new DragRotateAndZoom()]),
|
||||
target: "map",
|
||||
layers: [this.imageLayer, this.vectorLayer],
|
||||
view: this.createMapView(projection, this.imageExtent),
|
||||
});
|
||||
|
||||
this.map.on("pointerdown", this.handlePointerDown);
|
||||
this.map.on("pointermove", this.handlePointerMove);
|
||||
this.map.on("pointerup", this.handlePointerUp);
|
||||
}
|
||||
|
||||
private setImage = (imageUri: string, imageExtent: number[]) => {
|
||||
const projection = this.createProjection(imageExtent);
|
||||
this.imageLayer.setSource(this.createImageSource(imageUri, projection, imageExtent));
|
||||
const mapView = this.createMapView(projection, imageExtent);
|
||||
this.map.setView(mapView);
|
||||
}
|
||||
|
||||
private createProjection = (imageExtend: number[]) => {
|
||||
return new Projection({
|
||||
code: "xkcd-image",
|
||||
units: "pixels",
|
||||
extent: imageExtend,
|
||||
});
|
||||
}
|
||||
|
||||
private createMapView = (projection: Projection, imageExtend: number[]) => {
|
||||
const minZoom = this.getMinimumZoom();
|
||||
const rotation = (this.props.imageAngle)
|
||||
? Utils.degreeToRadians((this.props.imageAngle + 360) % 360)
|
||||
: 0;
|
||||
|
||||
return new View({
|
||||
projection,
|
||||
center: getCenter(imageExtend),
|
||||
rotation,
|
||||
zoom: minZoom,
|
||||
minZoom,
|
||||
});
|
||||
}
|
||||
|
||||
private createImageSource = (imageUri: string, projection: Projection, imageExtend: number[]) => {
|
||||
return new Static({
|
||||
url: imageUri,
|
||||
projection,
|
||||
imageExtent: imageExtend,
|
||||
});
|
||||
}
|
||||
|
||||
private getMinimumZoom = () => {
|
||||
// In openlayers, the image will be projected into 256x256 pixels,
|
||||
// and image will be 2x larger at each zoom level.
|
||||
// https://openlayers.org/en/latest/examples/min-zoom.html
|
||||
|
||||
const containerAspectRatio = (this.mapElement)
|
||||
? (this.mapElement.clientHeight / this.mapElement.clientWidth) : 1;
|
||||
const imageAspectRatio = this.props.imageHeight / this.props.imageWidth;
|
||||
if (imageAspectRatio > containerAspectRatio) {
|
||||
// Fit to width
|
||||
return Math.LOG2E * Math.log(this.mapElement!.clientHeight / 256);
|
||||
} else {
|
||||
// Fit to height
|
||||
return Math.LOG2E * Math.log(this.mapElement!.clientWidth / 256);
|
||||
}
|
||||
}
|
||||
|
||||
private handlePointerDown = (event: MapBrowserEvent) => {
|
||||
if (!this.props.enableFeatureSelection) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.countPointerDown += 1;
|
||||
if (this.countPointerDown >= 2) {
|
||||
this.setDragPanInteraction(true /*dragPanEnabled*/);
|
||||
this.isSwiping = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const isPointerOnFeature = this.map.hasFeatureAtPixel(
|
||||
this.map.getEventPixel(event.originalEvent),
|
||||
this.vectorLayerFilter);
|
||||
|
||||
if (isPointerOnFeature && this.props.handleFeatureSelect) {
|
||||
const eventPixel = this.map.getEventPixel(event.originalEvent);
|
||||
this.map.forEachFeatureAtPixel(
|
||||
eventPixel,
|
||||
(feature) => {
|
||||
if (this.props.handleFeatureSelect) {
|
||||
this.props.handleFeatureSelect(feature, true /*isTaggle*/);
|
||||
}
|
||||
},
|
||||
this.vectorLayerFilter);
|
||||
}
|
||||
|
||||
this.setDragPanInteraction(!isPointerOnFeature /*dragPanEnabled*/);
|
||||
this.isSwiping = isPointerOnFeature;
|
||||
}
|
||||
|
||||
private handlePointerMove = (event: MapBrowserEvent) => {
|
||||
if (this.shouldIgnorePointerMove()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// disable vertical scrolling for iOS Safari
|
||||
event.preventDefault();
|
||||
|
||||
const eventPixel = this.map.getEventPixel(event.originalEvent);
|
||||
this.map.forEachFeatureAtPixel(
|
||||
eventPixel,
|
||||
(feature) => {
|
||||
if (this.props.handleFeatureSelect) {
|
||||
this.props.handleFeatureSelect(feature, false /*isTaggle*/);
|
||||
}
|
||||
},
|
||||
this.vectorLayerFilter);
|
||||
}
|
||||
|
||||
private handlePointerUp = () => {
|
||||
if (!this.props.enableFeatureSelection) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.countPointerDown -= 1;
|
||||
if (this.countPointerDown === 0) {
|
||||
this.setDragPanInteraction(true /*dragPanEnabled*/);
|
||||
this.isSwiping = false;
|
||||
this.pointerMoveEventCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private setDragPanInteraction = (dragPanEnabled: boolean) => {
|
||||
this.map.getInteractions().forEach((interaction) => {
|
||||
if (interaction instanceof DragPan) {
|
||||
interaction.setActive(dragPanEnabled);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private shouldIgnorePointerMove = () => {
|
||||
if (!this.props.enableFeatureSelection) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!this.isSwiping) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.ignorePointerMoveEventCount > this.pointerMoveEventCount) {
|
||||
++this.pointerMoveEventCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { Feature, MapBrowserEvent, View } from "ol";
|
||||
import { Extent, getCenter } from "ol/extent";
|
||||
import { defaults as defaultInteractions, DragPan, DragRotateAndZoom, Interaction } from "ol/interaction.js";
|
||||
import ImageLayer from "ol/layer/Image";
|
||||
import Layer from "ol/layer/Layer";
|
||||
import VectorLayer from "ol/layer/Vector";
|
||||
import Map from "ol/Map";
|
||||
import Projection from "ol/proj/Projection";
|
||||
import Static from "ol/source/ImageStatic.js";
|
||||
import VectorSource from "ol/source/Vector";
|
||||
import * as React from "react";
|
||||
import "./styles.css";
|
||||
import Utils from "./utils";
|
||||
|
||||
interface IImageMapProps {
|
||||
imageUri: string;
|
||||
imageWidth: number;
|
||||
imageHeight: number;
|
||||
imageAngle?: number;
|
||||
|
||||
featureStyler?: any;
|
||||
|
||||
enableFeatureSelection?: boolean;
|
||||
handleFeatureSelect?: (feature: any, isTaggle: boolean) => void;
|
||||
|
||||
onMapReady: () => void;
|
||||
}
|
||||
|
||||
export class ImageMap extends React.Component<IImageMapProps> {
|
||||
private map: Map;
|
||||
private imageLayer: ImageLayer;
|
||||
private vectorLayer: VectorLayer;
|
||||
|
||||
private mapElement: HTMLDivElement | null = null;
|
||||
|
||||
private imageExtent: number[];
|
||||
|
||||
private countPointerDown: number = 0;
|
||||
private isSwiping: boolean = false;
|
||||
|
||||
private readonly VECTOR_LAYER_NAME = "vectorLayer";
|
||||
|
||||
private ignorePointerMoveEventCount: number = 5;
|
||||
private pointerMoveEventCount: number = 0;
|
||||
|
||||
private vectorLayerFilter = {
|
||||
layerFilter: (layer: Layer) => layer.get("name") === this.VECTOR_LAYER_NAME,
|
||||
};
|
||||
|
||||
constructor(props: IImageMapProps) {
|
||||
super(props);
|
||||
|
||||
this.imageExtent = [0, 0, this.props.imageWidth, this.props.imageHeight];
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
this.initMap();
|
||||
}
|
||||
|
||||
public componentDidUpdate(prevProps: IImageMapProps) {
|
||||
if (prevProps.imageUri !== this.props.imageUri) {
|
||||
this.imageExtent = [0, 0, this.props.imageWidth, this.props.imageHeight];
|
||||
this.setImage(this.props.imageUri, this.imageExtent);
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<div className="map-wrapper">
|
||||
<div id="map" className="map" ref={(el) => this.mapElement = el}></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one feature to the map
|
||||
*/
|
||||
public addFeature = (feature: Feature) => {
|
||||
this.vectorLayer.getSource().addFeature(feature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add features to the map
|
||||
*/
|
||||
public addFeatures = (features: Feature[]) => {
|
||||
this.vectorLayer.getSource().addFeatures(features);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add interaction to the map
|
||||
*/
|
||||
public addInteraction = (interaction: Interaction) => {
|
||||
this.map.addInteraction(interaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all features from the map
|
||||
*/
|
||||
public getAllFeatures = () => {
|
||||
return this.vectorLayer.getSource().getFeatures();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove specific feature object from the map
|
||||
*/
|
||||
public removeFeature = (feature: Feature) => {
|
||||
this.vectorLayer.getSource().removeFeature(feature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all features from the map
|
||||
*/
|
||||
public removeAllFeatures = () => {
|
||||
this.vectorLayer.getSource().clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove interaction from the map
|
||||
*/
|
||||
public removeInteraction = (interaction: Interaction) => {
|
||||
this.map.removeInteraction(interaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the image extent (left, top, right, bottom)
|
||||
*/
|
||||
public getImageExtent = () => {
|
||||
return this.imageExtent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get features at specific extend
|
||||
*/
|
||||
public getFeaturesInExtent = (extent: Extent): Feature[] => {
|
||||
const features: Feature[] = [];
|
||||
this.vectorLayer.getSource().forEachFeatureInExtent(extent, (feature) => {
|
||||
features.push(feature);
|
||||
});
|
||||
return features;
|
||||
}
|
||||
|
||||
private initMap = () => {
|
||||
const projection = this.createProjection(this.imageExtent);
|
||||
|
||||
this.imageLayer = new ImageLayer({
|
||||
source: this.createImageSource(this.props.imageUri, projection, this.imageExtent),
|
||||
});
|
||||
|
||||
const options: any = {};
|
||||
options.name = this.VECTOR_LAYER_NAME;
|
||||
options.style = this.props.featureStyler;
|
||||
options.source = new VectorSource();
|
||||
this.vectorLayer = new VectorLayer(options);
|
||||
|
||||
this.map = new Map({
|
||||
interactions: defaultInteractions({ doubleClickZoom: false }).extend([new DragRotateAndZoom()]),
|
||||
target: "map",
|
||||
layers: [this.imageLayer, this.vectorLayer],
|
||||
view: this.createMapView(projection, this.imageExtent),
|
||||
});
|
||||
|
||||
this.map.on("pointerdown", this.handlePointerDown);
|
||||
this.map.on("pointermove", this.handlePointerMove);
|
||||
this.map.on("pointerup", this.handlePointerUp);
|
||||
}
|
||||
|
||||
private setImage = (imageUri: string, imageExtent: number[]) => {
|
||||
const projection = this.createProjection(imageExtent);
|
||||
this.imageLayer.setSource(this.createImageSource(imageUri, projection, imageExtent));
|
||||
const mapView = this.createMapView(projection, imageExtent);
|
||||
this.map.setView(mapView);
|
||||
}
|
||||
|
||||
private createProjection = (imageExtend: number[]) => {
|
||||
return new Projection({
|
||||
code: "xkcd-image",
|
||||
units: "pixels",
|
||||
extent: imageExtend,
|
||||
});
|
||||
}
|
||||
|
||||
private createMapView = (projection: Projection, imageExtend: number[]) => {
|
||||
const minZoom = this.getMinimumZoom();
|
||||
const rotation = (this.props.imageAngle)
|
||||
? Utils.degreeToRadians((this.props.imageAngle + 360) % 360)
|
||||
: 0;
|
||||
|
||||
return new View({
|
||||
projection,
|
||||
center: getCenter(imageExtend),
|
||||
rotation,
|
||||
zoom: minZoom,
|
||||
minZoom,
|
||||
});
|
||||
}
|
||||
|
||||
private createImageSource = (imageUri: string, projection: Projection, imageExtend: number[]) => {
|
||||
return new Static({
|
||||
url: imageUri,
|
||||
projection,
|
||||
imageExtent: imageExtend,
|
||||
});
|
||||
}
|
||||
|
||||
private getMinimumZoom = () => {
|
||||
// In openlayers, the image will be projected into 256x256 pixels,
|
||||
// and image will be 2x larger at each zoom level.
|
||||
// https://openlayers.org/en/latest/examples/min-zoom.html
|
||||
|
||||
const containerAspectRatio = (this.mapElement)
|
||||
? (this.mapElement.clientHeight / this.mapElement.clientWidth) : 1;
|
||||
const imageAspectRatio = this.props.imageHeight / this.props.imageWidth;
|
||||
if (imageAspectRatio > containerAspectRatio) {
|
||||
// Fit to width
|
||||
return Math.LOG2E * Math.log(this.mapElement!.clientHeight / 256);
|
||||
} else {
|
||||
// Fit to height
|
||||
return Math.LOG2E * Math.log(this.mapElement!.clientWidth / 256);
|
||||
}
|
||||
}
|
||||
|
||||
private handlePointerDown = (event: MapBrowserEvent) => {
|
||||
if (!this.props.enableFeatureSelection) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.countPointerDown += 1;
|
||||
if (this.countPointerDown >= 2) {
|
||||
this.setDragPanInteraction(true /*dragPanEnabled*/);
|
||||
this.isSwiping = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const isPointerOnFeature = this.map.hasFeatureAtPixel(
|
||||
this.map.getEventPixel(event.originalEvent),
|
||||
this.vectorLayerFilter);
|
||||
|
||||
if (isPointerOnFeature && this.props.handleFeatureSelect) {
|
||||
const eventPixel = this.map.getEventPixel(event.originalEvent);
|
||||
this.map.forEachFeatureAtPixel(
|
||||
eventPixel,
|
||||
(feature) => {
|
||||
if (this.props.handleFeatureSelect) {
|
||||
this.props.handleFeatureSelect(feature, true /*isTaggle*/);
|
||||
}
|
||||
},
|
||||
this.vectorLayerFilter);
|
||||
}
|
||||
|
||||
this.setDragPanInteraction(!isPointerOnFeature /*dragPanEnabled*/);
|
||||
this.isSwiping = isPointerOnFeature;
|
||||
}
|
||||
|
||||
private handlePointerMove = (event: MapBrowserEvent) => {
|
||||
if (this.shouldIgnorePointerMove()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// disable vertical scrolling for iOS Safari
|
||||
event.preventDefault();
|
||||
|
||||
const eventPixel = this.map.getEventPixel(event.originalEvent);
|
||||
this.map.forEachFeatureAtPixel(
|
||||
eventPixel,
|
||||
(feature) => {
|
||||
if (this.props.handleFeatureSelect) {
|
||||
this.props.handleFeatureSelect(feature, false /*isTaggle*/);
|
||||
}
|
||||
},
|
||||
this.vectorLayerFilter);
|
||||
}
|
||||
|
||||
private handlePointerUp = () => {
|
||||
if (!this.props.enableFeatureSelection) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.countPointerDown -= 1;
|
||||
if (this.countPointerDown === 0) {
|
||||
this.setDragPanInteraction(true /*dragPanEnabled*/);
|
||||
this.isSwiping = false;
|
||||
this.pointerMoveEventCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private setDragPanInteraction = (dragPanEnabled: boolean) => {
|
||||
this.map.getInteractions().forEach((interaction) => {
|
||||
if (interaction instanceof DragPan) {
|
||||
interaction.setActive(dragPanEnabled);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private shouldIgnorePointerMove = () => {
|
||||
if (!this.props.enableFeatureSelection) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!this.isSwiping) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.ignorePointerMoveEventCount > this.pointerMoveEventCount) {
|
||||
++this.pointerMoveEventCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
export default class Utils {
|
||||
// convert degree to radians
|
||||
public static degreeToRadians(degree: number) {
|
||||
return degree * Math.PI * 2 / 360;
|
||||
}
|
||||
}
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
export default class Utils {
|
||||
// convert degree to radians
|
||||
public static degreeToRadians(degree: number) {
|
||||
return degree * Math.PI * 2 / 360;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,92 +1,92 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { Action } from "redux";
|
||||
import { ActionTypes } from "./actionTypes";
|
||||
import {
|
||||
ISaveAppSettingsAction,
|
||||
IEnsureSecurityTokenAction,
|
||||
} from "./applicationActions";
|
||||
import {
|
||||
ILoadConnectionAction,
|
||||
ISaveConnectionAction,
|
||||
IDeleteConnectionAction,
|
||||
} from "./connectionActions";
|
||||
import {
|
||||
ILoadProjectAction,
|
||||
ICloseProjectAction,
|
||||
ISaveProjectAction,
|
||||
ILoadProjectAssetsAction,
|
||||
ISaveAssetMetadataAction,
|
||||
ILoadAssetMetadataAction,
|
||||
IDeleteProjectAction,
|
||||
} from "./projectActions";
|
||||
import {
|
||||
IShowAppErrorAction,
|
||||
IClearErrorAction,
|
||||
} from "./appErrorActions";
|
||||
import { ISetTitleAction } from "./appTitleActions";
|
||||
|
||||
/**
|
||||
* Data payload dispatched from the action and delivered to reducer
|
||||
*/
|
||||
export interface IPayloadAction<TType, TPayload> extends Action<TType> {
|
||||
payload: TPayload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates action and validates type of action type name
|
||||
* @param type Name for action being created
|
||||
*/
|
||||
// tslint:disable-next-line:max-line-length
|
||||
export function createAction<TAction extends Action<TAction["type"]>>(type: TAction["type"]): () => Action<TAction["type"]> {
|
||||
return () => ({
|
||||
type,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create action with payload
|
||||
* @param type Name for action being created
|
||||
*/
|
||||
// tslint:disable-next-line:max-line-length
|
||||
export function createPayloadAction<TAction extends IPayloadAction<TAction["type"], TAction["payload"]>>(type: TAction["type"]): (payload: TAction["payload"]) => IPayloadAction<TAction["type"], TAction["payload"]> {
|
||||
return (payload: TAction["payload"]) => ({
|
||||
type,
|
||||
payload,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Catch-all for unregistered actions
|
||||
*/
|
||||
export interface IOtherAction extends Action<string> {
|
||||
type: ActionTypes.ANY_OTHER_ACTION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper instance of catch-all
|
||||
*/
|
||||
export const anyOtherAction = createAction<IOtherAction>(ActionTypes.ANY_OTHER_ACTION);
|
||||
|
||||
/**
|
||||
* Used by reducers to type-check all actions
|
||||
*/
|
||||
export type AnyAction = IOtherAction |
|
||||
ISaveAppSettingsAction |
|
||||
IEnsureSecurityTokenAction |
|
||||
ISaveConnectionAction |
|
||||
IDeleteConnectionAction |
|
||||
ILoadConnectionAction |
|
||||
ISaveConnectionAction |
|
||||
IDeleteConnectionAction |
|
||||
ILoadProjectAction |
|
||||
ICloseProjectAction |
|
||||
ISaveProjectAction |
|
||||
IDeleteProjectAction |
|
||||
ILoadProjectAssetsAction |
|
||||
ISaveAssetMetadataAction |
|
||||
ILoadAssetMetadataAction |
|
||||
IShowAppErrorAction |
|
||||
IClearErrorAction |
|
||||
ISetTitleAction;
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { Action } from "redux";
|
||||
import { ActionTypes } from "./actionTypes";
|
||||
import {
|
||||
ISaveAppSettingsAction,
|
||||
IEnsureSecurityTokenAction,
|
||||
} from "./applicationActions";
|
||||
import {
|
||||
ILoadConnectionAction,
|
||||
ISaveConnectionAction,
|
||||
IDeleteConnectionAction,
|
||||
} from "./connectionActions";
|
||||
import {
|
||||
ILoadProjectAction,
|
||||
ICloseProjectAction,
|
||||
ISaveProjectAction,
|
||||
ILoadProjectAssetsAction,
|
||||
ISaveAssetMetadataAction,
|
||||
ILoadAssetMetadataAction,
|
||||
IDeleteProjectAction,
|
||||
} from "./projectActions";
|
||||
import {
|
||||
IShowAppErrorAction,
|
||||
IClearErrorAction,
|
||||
} from "./appErrorActions";
|
||||
import { ISetTitleAction } from "./appTitleActions";
|
||||
|
||||
/**
|
||||
* Data payload dispatched from the action and delivered to reducer
|
||||
*/
|
||||
export interface IPayloadAction<TType, TPayload> extends Action<TType> {
|
||||
payload: TPayload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates action and validates type of action type name
|
||||
* @param type Name for action being created
|
||||
*/
|
||||
// tslint:disable-next-line:max-line-length
|
||||
export function createAction<TAction extends Action<TAction["type"]>>(type: TAction["type"]): () => Action<TAction["type"]> {
|
||||
return () => ({
|
||||
type,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create action with payload
|
||||
* @param type Name for action being created
|
||||
*/
|
||||
// tslint:disable-next-line:max-line-length
|
||||
export function createPayloadAction<TAction extends IPayloadAction<TAction["type"], TAction["payload"]>>(type: TAction["type"]): (payload: TAction["payload"]) => IPayloadAction<TAction["type"], TAction["payload"]> {
|
||||
return (payload: TAction["payload"]) => ({
|
||||
type,
|
||||
payload,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Catch-all for unregistered actions
|
||||
*/
|
||||
export interface IOtherAction extends Action<string> {
|
||||
type: ActionTypes.ANY_OTHER_ACTION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper instance of catch-all
|
||||
*/
|
||||
export const anyOtherAction = createAction<IOtherAction>(ActionTypes.ANY_OTHER_ACTION);
|
||||
|
||||
/**
|
||||
* Used by reducers to type-check all actions
|
||||
*/
|
||||
export type AnyAction = IOtherAction |
|
||||
ISaveAppSettingsAction |
|
||||
IEnsureSecurityTokenAction |
|
||||
ISaveConnectionAction |
|
||||
IDeleteConnectionAction |
|
||||
ILoadConnectionAction |
|
||||
ISaveConnectionAction |
|
||||
IDeleteConnectionAction |
|
||||
ILoadProjectAction |
|
||||
ICloseProjectAction |
|
||||
ISaveProjectAction |
|
||||
IDeleteProjectAction |
|
||||
ILoadProjectAssetsAction |
|
||||
ISaveAssetMetadataAction |
|
||||
ILoadAssetMetadataAction |
|
||||
IShowAppErrorAction |
|
||||
IClearErrorAction |
|
||||
ISetTitleAction;
|
||||
|
|
|
@ -1,36 +1,36 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
* Redux Action types
|
||||
*/
|
||||
export enum ActionTypes {
|
||||
// App
|
||||
SAVE_APP_SETTINGS_SUCCESS = "SAVE_APP_SETTINGS_SUCCESS",
|
||||
ENSURE_SECURITY_TOKEN_SUCCESS = "ENSURE_SECURITY_TOKEN_SUCCESS",
|
||||
|
||||
// Projects
|
||||
LOAD_PROJECT_SUCCESS = "LOAD_PROJECT_SUCCESS",
|
||||
SAVE_PROJECT_SUCCESS = "SAVE_PROJECT_SUCCESS",
|
||||
DELETE_PROJECT_SUCCESS = "DELETE_PROJECT_SUCCESS",
|
||||
CLOSE_PROJECT_SUCCESS = "CLOSE_PROJECT_SUCCESS",
|
||||
LOAD_PROJECT_ASSETS_SUCCESS = "LOAD_PROJECT_ASSETS_SUCCESS",
|
||||
UPDATE_PROJECT_TAG_SUCCESS = "UPDATE_PROJECT_TAG_SUCCESS",
|
||||
DELETE_PROJECT_TAG_SUCCESS = "DELETE_PROJECT_TAG_SUCCESS",
|
||||
|
||||
// Connections
|
||||
LOAD_CONNECTION_SUCCESS = "LOAD_CONNECTION_SUCCESS",
|
||||
SAVE_CONNECTION_SUCCESS = "SAVE_CONNECTION_SUCCESS",
|
||||
DELETE_CONNECTION_SUCCESS = "DELETE_CONNECTION_SUCCESS",
|
||||
|
||||
// Assets
|
||||
SAVE_ASSET_METADATA_SUCCESS = "SAVE_ASSET_METADATA_SUCCESS",
|
||||
LOAD_ASSET_METADATA_SUCCESS = "LOAD_ASSET_METADATA_SUCCESS",
|
||||
|
||||
ANY_OTHER_ACTION = "ANY_OTHER_ACTION_SUCCESS",
|
||||
|
||||
SHOW_ERROR = "SHOW_ERROR",
|
||||
CLEAR_ERROR = "CLEAR_ERROR",
|
||||
|
||||
SET_TITLE = "SET_TITLE",
|
||||
}
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
/**
|
||||
* Redux Action types
|
||||
*/
|
||||
export enum ActionTypes {
|
||||
// App
|
||||
SAVE_APP_SETTINGS_SUCCESS = "SAVE_APP_SETTINGS_SUCCESS",
|
||||
ENSURE_SECURITY_TOKEN_SUCCESS = "ENSURE_SECURITY_TOKEN_SUCCESS",
|
||||
|
||||
// Projects
|
||||
LOAD_PROJECT_SUCCESS = "LOAD_PROJECT_SUCCESS",
|
||||
SAVE_PROJECT_SUCCESS = "SAVE_PROJECT_SUCCESS",
|
||||
DELETE_PROJECT_SUCCESS = "DELETE_PROJECT_SUCCESS",
|
||||
CLOSE_PROJECT_SUCCESS = "CLOSE_PROJECT_SUCCESS",
|
||||
LOAD_PROJECT_ASSETS_SUCCESS = "LOAD_PROJECT_ASSETS_SUCCESS",
|
||||
UPDATE_PROJECT_TAG_SUCCESS = "UPDATE_PROJECT_TAG_SUCCESS",
|
||||
DELETE_PROJECT_TAG_SUCCESS = "DELETE_PROJECT_TAG_SUCCESS",
|
||||
|
||||
// Connections
|
||||
LOAD_CONNECTION_SUCCESS = "LOAD_CONNECTION_SUCCESS",
|
||||
SAVE_CONNECTION_SUCCESS = "SAVE_CONNECTION_SUCCESS",
|
||||
DELETE_CONNECTION_SUCCESS = "DELETE_CONNECTION_SUCCESS",
|
||||
|
||||
// Assets
|
||||
SAVE_ASSET_METADATA_SUCCESS = "SAVE_ASSET_METADATA_SUCCESS",
|
||||
LOAD_ASSET_METADATA_SUCCESS = "LOAD_ASSET_METADATA_SUCCESS",
|
||||
|
||||
ANY_OTHER_ACTION = "ANY_OTHER_ACTION_SUCCESS",
|
||||
|
||||
SHOW_ERROR = "SHOW_ERROR",
|
||||
CLEAR_ERROR = "CLEAR_ERROR",
|
||||
|
||||
SET_TITLE = "SET_TITLE",
|
||||
}
|
||||
|
|
|
@ -1,64 +1,64 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { Dispatch, Action } from "redux";
|
||||
import { IAppError } from "../../models/applicationState";
|
||||
import { createPayloadAction, IPayloadAction, createAction } from "./actionCreators";
|
||||
import { ActionTypes } from "./actionTypes";
|
||||
|
||||
/**
|
||||
* Action to display alert when there's an error in the app
|
||||
* @member showError
|
||||
* @member clearError
|
||||
* @interface
|
||||
*/
|
||||
export default interface IAppErrorActions {
|
||||
showError(appError: IAppError): void;
|
||||
clearError(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* show alert popup to indicate error
|
||||
* @param appError {IAppError} the error to display in alert
|
||||
* @returns {(dispatch: Dispatch) => void}
|
||||
*/
|
||||
export function showError(appError: IAppError): (dispatch: Dispatch) => void {
|
||||
return (dispatch: Dispatch) => {
|
||||
dispatch(showErrorAction(appError));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* clear alert popup
|
||||
* @returns {(dispatch: Dispatch) => void}
|
||||
*/
|
||||
export function clearError(): (dispatch: Dispatch) => void {
|
||||
return (dispatch: Dispatch) => {
|
||||
dispatch(clearErrorAction());
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Show error action type
|
||||
*/
|
||||
export interface IShowAppErrorAction extends IPayloadAction<string, IAppError> {
|
||||
type: ActionTypes.SHOW_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear error action type
|
||||
*/
|
||||
export interface IClearErrorAction extends Action<string> {
|
||||
type: ActionTypes.CLEAR_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instance of show error action
|
||||
*/
|
||||
export const showErrorAction = createPayloadAction<IShowAppErrorAction>(ActionTypes.SHOW_ERROR);
|
||||
|
||||
/**
|
||||
* Instance of clear error action
|
||||
* @type {() => Action<IClearErrorAction["type"]>}
|
||||
*/
|
||||
export const clearErrorAction = createAction<IClearErrorAction>(ActionTypes.CLEAR_ERROR);
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { Dispatch, Action } from "redux";
|
||||
import { IAppError } from "../../models/applicationState";
|
||||
import { createPayloadAction, IPayloadAction, createAction } from "./actionCreators";
|
||||
import { ActionTypes } from "./actionTypes";
|
||||
|
||||
/**
|
||||
* Action to display alert when there's an error in the app
|
||||
* @member showError
|
||||
* @member clearError
|
||||
* @interface
|
||||
*/
|
||||
export default interface IAppErrorActions {
|
||||
showError(appError: IAppError): void;
|
||||
clearError(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* show alert popup to indicate error
|
||||
* @param appError {IAppError} the error to display in alert
|
||||
* @returns {(dispatch: Dispatch) => void}
|
||||
*/
|
||||
export function showError(appError: IAppError): (dispatch: Dispatch) => void {
|
||||
return (dispatch: Dispatch) => {
|
||||
dispatch(showErrorAction(appError));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* clear alert popup
|
||||
* @returns {(dispatch: Dispatch) => void}
|
||||
*/
|
||||
export function clearError(): (dispatch: Dispatch) => void {
|
||||
return (dispatch: Dispatch) => {
|
||||
dispatch(clearErrorAction());
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Show error action type
|
||||
*/
|
||||
export interface IShowAppErrorAction extends IPayloadAction<string, IAppError> {
|
||||
type: ActionTypes.SHOW_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear error action type
|
||||
*/
|
||||
export interface IClearErrorAction extends Action<string> {
|
||||
type: ActionTypes.CLEAR_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instance of show error action
|
||||
*/
|
||||
export const showErrorAction = createPayloadAction<IShowAppErrorAction>(ActionTypes.SHOW_ERROR);
|
||||
|
||||
/**
|
||||
* Instance of clear error action
|
||||
* @type {() => Action<IClearErrorAction["type"]>}
|
||||
*/
|
||||
export const clearErrorAction = createAction<IClearErrorAction>(ActionTypes.CLEAR_ERROR);
|
||||
|
|
|
@ -1,38 +1,38 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { Dispatch } from "redux";
|
||||
import { createPayloadAction, IPayloadAction } from "./actionCreators";
|
||||
import { ActionTypes } from "./actionTypes";
|
||||
|
||||
/**
|
||||
* Action to set app title
|
||||
* @member setTitle
|
||||
* @interface
|
||||
*/
|
||||
export default interface IAppTitleActions {
|
||||
setTitle(title: string): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* set app title
|
||||
* @param title {string} the title of app
|
||||
* @returns {(dispatch: Dispatch) => void}
|
||||
*/
|
||||
export function setTitle(title: string): (dispatch: Dispatch) => void {
|
||||
return (dispatch: Dispatch) => {
|
||||
dispatch(setTitleAction(title));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set app title
|
||||
*/
|
||||
export interface ISetTitleAction extends IPayloadAction<string, string> {
|
||||
type: ActionTypes.SET_TITLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instance of set title action
|
||||
*/
|
||||
export const setTitleAction = createPayloadAction<ISetTitleAction>(ActionTypes.SET_TITLE);
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { Dispatch } from "redux";
|
||||
import { createPayloadAction, IPayloadAction } from "./actionCreators";
|
||||
import { ActionTypes } from "./actionTypes";
|
||||
|
||||
/**
|
||||
* Action to set app title
|
||||
* @member setTitle
|
||||
* @interface
|
||||
*/
|
||||
export default interface IAppTitleActions {
|
||||
setTitle(title: string): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* set app title
|
||||
* @param title {string} the title of app
|
||||
* @returns {(dispatch: Dispatch) => void}
|
||||
*/
|
||||
export function setTitle(title: string): (dispatch: Dispatch) => void {
|
||||
return (dispatch: Dispatch) => {
|
||||
dispatch(setTitleAction(title));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set app title
|
||||
*/
|
||||
export interface ISetTitleAction extends IPayloadAction<string, string> {
|
||||
type: ActionTypes.SET_TITLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instance of set title action
|
||||
*/
|
||||
export const setTitleAction = createPayloadAction<ISetTitleAction>(ActionTypes.SET_TITLE);
|
||||
|
|
|
@ -1,29 +1,29 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { reducer } from "./appTitleReducer";
|
||||
import { setTitleAction } from "../actions/appTitleActions";
|
||||
import { anyOtherAction } from "../actions/actionCreators";
|
||||
|
||||
describe("AppTitle Reducer", () => {
|
||||
let state: string;
|
||||
|
||||
beforeEach(() => {
|
||||
state = "Welcome";
|
||||
});
|
||||
|
||||
it("SetTitle discard previous state and return a title", () => {
|
||||
const title = "Hello";
|
||||
const action = setTitleAction(title);
|
||||
|
||||
const result = reducer(state, action);
|
||||
expect(result).not.toEqual(state);
|
||||
expect(result).toEqual(title);
|
||||
});
|
||||
|
||||
it("Unknown action performs noop", () => {
|
||||
const action = anyOtherAction();
|
||||
const result = reducer(state, action);
|
||||
expect(result).toBe(state);
|
||||
});
|
||||
});
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { reducer } from "./appTitleReducer";
|
||||
import { setTitleAction } from "../actions/appTitleActions";
|
||||
import { anyOtherAction } from "../actions/actionCreators";
|
||||
|
||||
describe("AppTitle Reducer", () => {
|
||||
let state: string;
|
||||
|
||||
beforeEach(() => {
|
||||
state = "Welcome";
|
||||
});
|
||||
|
||||
it("SetTitle discard previous state and return a title", () => {
|
||||
const title = "Hello";
|
||||
const action = setTitleAction(title);
|
||||
|
||||
const result = reducer(state, action);
|
||||
expect(result).not.toEqual(state);
|
||||
expect(result).toEqual(title);
|
||||
});
|
||||
|
||||
it("Unknown action performs noop", () => {
|
||||
const action = anyOtherAction();
|
||||
const result = reducer(state, action);
|
||||
expect(result).toBe(state);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { ActionTypes } from "../actions/actionTypes";
|
||||
import { AnyAction } from "../actions/actionCreators";
|
||||
|
||||
/**
|
||||
* App Title Reducer
|
||||
* Actions handled:
|
||||
* SET_TITLE
|
||||
* @param {string} state
|
||||
* @param {AnyAction} action
|
||||
* @returns {any}
|
||||
*/
|
||||
export const reducer = (state: string = null, action: AnyAction) => {
|
||||
switch (action.type) {
|
||||
case ActionTypes.SET_TITLE:
|
||||
return action.payload;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { ActionTypes } from "../actions/actionTypes";
|
||||
import { AnyAction } from "../actions/actionCreators";
|
||||
|
||||
/**
|
||||
* App Title Reducer
|
||||
* Actions handled:
|
||||
* SET_TITLE
|
||||
* @param {string} state
|
||||
* @param {AnyAction} action
|
||||
* @returns {any}
|
||||
*/
|
||||
export const reducer = (state: string = null, action: AnyAction) => {
|
||||
switch (action.type) {
|
||||
case ActionTypes.SET_TITLE:
|
||||
return action.payload;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
"ordered-imports": false,
|
||||
"no-string-literal": false,
|
||||
"no-bitwise": false,
|
||||
"function-constructor": false
|
||||
"function-constructor": false,
|
||||
"linebreak-style": [true, "LF"]
|
||||
},
|
||||
"rulesDirectory": []
|
||||
}
|
Загрузка…
Ссылка в новой задаче