Skip to content

Commit

Permalink
cleanup
Browse files Browse the repository at this point in the history
fix some type issues
remove some excess actions
document some stuff
  • Loading branch information
oatkiller committed Dec 20, 2019
1 parent 8fb9090 commit b0c6385
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 115 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export class ResolverEmbeddable extends Embeddable {
}
this.lastRenderTarget = node;
// TODO, figure out how to destroy middleware
const { store } = storeFactory({ httpServiceBase: this.httpService });
const { store } = storeFactory({ httpService: this.httpService });
ReactDOM.render(<AppRoot store={store} />, node);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@
*/

import { i18n } from '@kbn/i18n';
import { EmbeddableFactory, EmbeddableInput, IContainer } from 'src/plugins/embeddable/public';
import { HttpSetup } from 'kibana/public';
import { ResolverEmbeddable } from './';
import {
EmbeddableFactory,
IContainer,
EmbeddableInput,
} from '../../../../../../src/plugins/embeddable/public';
import { ResolverEmbeddable } from './embeddable';

export class ResolverEmbeddableFactory extends EmbeddableFactory {
public readonly type = 'resolver';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,13 @@ interface UserSetPanningOffset {
interface UserStartedPanning {
readonly type: 'userStartedPanning';
/**
* A vector in screen coordinates (each unit is a pixel and the Y axis increases towards the bottom of the screen.)
* A vector in screen coordinates (each unit is a pixel and the Y axis increases towards the bottom of the screen)
* relative to the Resolver component.
* Represents a starting position during panning for a pointing device.
*/
readonly payload: Vector2;
}

interface UserContinuedPanning {
readonly type: 'userContinuedPanning';
/**
* A vector in screen coordinates (each unit is a pixel and the Y axis increases towards the bottom of the screen.)
* Represents the current position during panning for a pointing device.
*/
readonly payload: Vector2;
}

interface UserStoppedPanning {
readonly type: 'userStoppedPanning';
}
Expand All @@ -67,12 +59,11 @@ interface UserCanceledPanning {
readonly type: 'userCanceledPanning';
}

// This action is blacklisted in redux dev tools
interface UserFocusedOnWorldCoordinates {
readonly type: 'userFocusedOnWorldCoordinates';
interface UserMovedPointer {
readonly type: 'userMovedPointer';
/**
* World coordinates indicating a point that the user's pointing device is hoving over.
* When the camera's scale is changed, we make sure to adjust its tranform so that the these world coordinates are in the same place on the screen
* A vector in screen coordinates relative to the Resolver component.
* The payload should be contain clientX and clientY minus the client position of the Resolver component.
*/
readonly payload: Vector2;
}
Expand All @@ -82,8 +73,7 @@ export type CameraAction =
| UserSetRasterSize
| UserSetPanningOffset
| UserStartedPanning
| UserContinuedPanning
| UserStoppedPanning
| UserCanceledPanning
| UserFocusedOnWorldCoordinates
| UserZoomed;
| UserZoomed
| UserMovedPointer;
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ describe('panning interaction', () => {
});
describe('when the user continues to pan 50px up and to the right', () => {
beforeEach(() => {
const action: CameraAction = { type: 'userContinuedPanning', payload: [150, 50] };
const action: CameraAction = { type: 'userMovedPointer', payload: [150, 50] };
store.dispatch(action);
});
it('should have a translation of 50,50', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,19 +73,6 @@ export const cameraReducer: Reducer<CameraState, ResolverAction> = (
currentOffset: action.payload,
},
};
} else if (action.type === 'userContinuedPanning') {
if (userIsPanning(state)) {
return {
// This logic means, if the user calls `userContinuedPanning` without starting panning first, we start automatically basically?
...state,
panning: {
origin: state.panning ? state.panning.origin : action.payload,
currentOffset: action.payload,
},
};
} else {
return state;
}
} else if (action.type === 'userStoppedPanning') {
if (userIsPanning(state)) {
return {
Expand All @@ -106,10 +93,25 @@ export const cameraReducer: Reducer<CameraState, ResolverAction> = (
...state,
rasterSize: action.payload,
};
} else if (action.type === 'userFocusedOnWorldCoordinates') {
} else if (action.type === 'userMovedPointer') {
return {
...state,
latestFocusedWorldCoordinates: action.payload,
/**
* keep track of the last world coordinates the user moved over.
* When the scale of the projection matrix changes, we adjust the camera's world transform in order
* to keep the same point under the pointer.
* In order to do this, we need to know the position of the mouse when changing the scale.
*/
latestFocusedWorldCoordinates: applyMatrix3(action.payload, inverseProjectionMatrix(state)),
/**
* If the user is panning, adjust the panning offset
*/
panning: userIsPanning(state)
? {
origin: state.panning ? state.panning.origin : action.payload,
currentOffset: action.payload,
}
: state.panning,
};
} else {
return state;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,10 @@ describe('zooming', () => {
});
describe('when the user has moved their mouse to the raster position 200, 50', () => {
beforeEach(() => {
// TODO update action
const action: CameraAction = {
type: 'userFocusedOnWorldCoordinates',
payload: applyMatrix3([200, 50], inverseProjectionMatrix(store.getState())),
type: 'userMovedPointer',
payload: [200, 50],
};
store.dispatch(action);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,26 @@
*/

import { createStore, StoreEnhancer } from 'redux';
import { ResolverAction } from '../types';
import { HttpSetup } from '../../../../../../../src/core/public';
import { resolverReducer } from './reducer';
import { HttpServiceBase } from '../../../../../../../src/core/public';

export const storeFactory = ({ httpServiceBase }: { httpServiceBase: HttpServiceBase }) => {
export const storeFactory = (_dependencies: { httpService: HttpSetup }) => {
interface SomethingThatMightHaveReduxDevTools {
__REDUX_DEVTOOLS_EXTENSION__?: (options?: {
name?: string;
actionsBlacklist: readonly string[];
}) => StoreEnhancer;
}
const windowWhichMightHaveReduxDevTools = window as SomethingThatMightHaveReduxDevTools;
// Make sure blacklisted action types are valid
const actionsBlacklist: ReadonlyArray<ResolverAction['type']> = ['userMovedPointer'];
const store = createStore(
resolverReducer,
windowWhichMightHaveReduxDevTools.__REDUX_DEVTOOLS_EXTENSION__ &&
windowWhichMightHaveReduxDevTools.__REDUX_DEVTOOLS_EXTENSION__({
name: 'Resolver',
actionsBlacklist: ['userFocusedOnWorldCoordinates'],
actionsBlacklist,
})
);
return {
Expand Down
143 changes: 70 additions & 73 deletions x-pack/plugins/endpoint/public/embeddables/resolver/view/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,17 @@ const Diagnostic = styled(

const [elementBoundingClientRect, clientRectCallback] = useAutoUpdatingClientRect();

const inverseProjectionMatrix = useSelector(selectors.inverseProjectionMatrix);

const worldPositionFromClientPosition = useCallback(
(clientPosition: Vector2): Vector2 | null => {
const relativeCoordinatesFromMouseEvent = useCallback(
(event: { clientX: number; clientY: number }): null | [number, number] => {
if (elementBoundingClientRect === undefined) {
return null;
}
return applyMatrix3(
[
clientPosition[0] - elementBoundingClientRect.x,
clientPosition[1] - elementBoundingClientRect.y,
],
inverseProjectionMatrix
);
return [
event.clientX - elementBoundingClientRect.x,
event.clientY - elementBoundingClientRect.y,
];
},
[inverseProjectionMatrix, elementBoundingClientRect]
[elementBoundingClientRect]
);

useEffect(() => {
Expand All @@ -59,35 +54,28 @@ const Diagnostic = styled(

const handleMouseDown = useCallback(
(event: React.MouseEvent<HTMLDivElement>) => {
dispatch({
type: 'userStartedPanning',
payload: [event.clientX, event.clientY],
});
const maybeCoordinates = relativeCoordinatesFromMouseEvent(event);
if (maybeCoordinates !== null) {
dispatch({
type: 'userStartedPanning',
payload: maybeCoordinates,
});
}
},
[dispatch]
[dispatch, relativeCoordinatesFromMouseEvent]
);

const handleMouseMove = useCallback(
(event: MouseEvent) => {
if (event.buttons === 1 && userIsPanning) {
const maybeCoordinates = relativeCoordinatesFromMouseEvent(event);
if (maybeCoordinates) {
dispatch({
type: 'userContinuedPanning',
payload: [event.clientX, event.clientY],
});
}
// TODO, don't fire two actions here. make userContinuedPanning also pass world position
const maybeClientWorldPosition = worldPositionFromClientPosition([
event.clientX,
event.clientY,
]);
if (maybeClientWorldPosition !== null) {
dispatch({
type: 'userFocusedOnWorldCoordinates',
payload: maybeClientWorldPosition,
type: 'userMovedPointer',
payload: maybeCoordinates,
});
}
},
[dispatch, userIsPanning, worldPositionFromClientPosition]
[dispatch, relativeCoordinatesFromMouseEvent]
);

const handleMouseUp = useCallback(() => {
Expand Down Expand Up @@ -131,8 +119,6 @@ const Diagnostic = styled(
};
}, [handleMouseMove]);

// TODO, handle mouse up when no longer on element or event window. ty

const dotPositions = useMemo(
(): ReadonlyArray<readonly [number, number]> => [
[0, 0],
Expand All @@ -156,16 +142,7 @@ const Diagnostic = styled(
[clientRectCallback]
);

useEffect(() => {
// Set the 'wheel' event listener directly on the element
// React sets event listeners on the window and routes them back via event propagation. As of Chrome 73 or something, 'wheel' events on the 'window' are automatically treated as 'passive'. Seems weird, but whatever
if (ref !== null) {
ref.addEventListener('wheel', handleWheel);
return () => {
ref.removeEventListener('wheel', handleWheel);
};
}
}, [handleWheel, ref]);
useNonPassiveWheelHandler(handleWheel, ref);

return (
<div className={className} ref={refCallback} onMouseDown={handleMouseDown}>
Expand All @@ -182,6 +159,36 @@ const Diagnostic = styled(
position: relative;
`;

const DiagnosticDot = styled(
React.memo(({ className, worldPosition }: { className?: string; worldPosition: Vector2 }) => {
const projectionMatrix = useSelector(selectors.projectionMatrix);
const [left, top] = applyMatrix3(worldPosition, projectionMatrix);
const style = {
left: (left - 20).toString() + 'px',
top: (top - 20).toString() + 'px',
};
return (
<span className={className} style={style}>
x: {worldPosition[0]}
<br />
y: {worldPosition[1]}
</span>
);
})
)`
position: absolute;
width: 40px;
height: 40px;
text-align: left;
font-size: 10px;
user-select: none;
border: 1px solid black;
box-sizing: border-box;
border-radius: 10%;
padding: 4px;
white-space: nowrap;
`;

/**
* Returns a DOMRect sometimes, and a `ref` callback. Put the `ref` as the `ref` property of an element, and
* DOMRect will be the result of getBoundingClientRect on it.
Expand Down Expand Up @@ -215,32 +222,22 @@ function useAutoUpdatingClientRect(): [DOMRect | undefined, (node: Element | nul
return [rect, ref];
}

const DiagnosticDot = styled(
React.memo(({ className, worldPosition }: { className?: string; worldPosition: Vector2 }) => {
const projectionMatrix = useSelector(selectors.projectionMatrix);
const [left, top] = applyMatrix3(worldPosition, projectionMatrix);
const style = {
left: (left - 20).toString() + 'px',
top: (top - 20).toString() + 'px',
};
return (
<span className={className} style={style}>
x: {worldPosition[0]}
<br />
y: {worldPosition[1]}
</span>
);
})
)`
position: absolute;
width: 40px;
height: 40px;
text-align: left;
font-size: 10px;
user-select: none;
border: 1px solid black;
box-sizing: border-box;
border-radius: 10%;
padding: 4px;
white-space: nowrap;
`;
/**
* Register an event handler directly on `elementRef` for the `wheel` event, with no options
* React sets native event listeners on the `window` and calls provided handlers via event propagation.
* As of Chrome 73, `'wheel'` events on `window` are automatically treated as 'passive'.
* If you don't need to call `event.preventDefault` then you should use regular React event handling instead.
*/
function useNonPassiveWheelHandler(
handler: (event: WheelEvent) => void,
elementRef: HTMLElement | null
) {
useEffect(() => {
if (elementRef !== null) {
elementRef.addEventListener('wheel', handler);
return () => {
elementRef.removeEventListener('wheel', handler);
};
}
}, [elementRef, handler]);
}

0 comments on commit b0c6385

Please sign in to comment.