Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions src/components/shared/Reorderable.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ type Props = {|
// See https://flow.org/en/docs/react/children/ for more information.
// Be careful: children need to handle a `style` property.
children: React.ChildrenArray<React.Element<any>>,
// If present, this will be attached to the container added for these
// children. As a reminder, the container will use the tagName defined above.
innerElementRef?: React.Ref<any>,
|};

type State = {|
Expand Down Expand Up @@ -240,15 +243,19 @@ export class Reorderable extends React.PureComponent<Props, State> {
}

render() {
const { className, order } = this.props;
const { className, order, innerElementRef } = this.props;
const children = React.Children.toArray(this.props.children);
const orderedChildren = order.map((childIndex) => children[childIndex]);
const TagName = this.props.tagName;
const xy = this._getXY();

if (this.state.phase === 'RESTING') {
return (
<TagName className={className} onMouseDown={this._onMouseDown}>
<TagName
className={className}
onMouseDown={this._onMouseDown}
ref={innerElementRef}
>
{orderedChildren}
</TagName>
);
Expand All @@ -261,8 +268,9 @@ export class Reorderable extends React.PureComponent<Props, State> {
adjustPrecedingBy,
adjustSucceedingBy,
} = this.state;

return (
<TagName className={className}>
<TagName className={className} ref={innerElementRef}>
{orderedChildren.map((child, childIndex) => {
const style = {
transition: '200ms ease-in-out transform',
Expand Down
12 changes: 9 additions & 3 deletions src/components/timeline/ActiveTabTimeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ import type {

import type { ConnectedProps } from 'firefox-profiler/utils/connect';

type OwnProps = {|
// This ref will be added to the inner container.
+innerElementRef?: React.Ref<any>,
|};

type StateProps = {|
+committedRange: StartEndRange,
+globalTracks: ActiveTabGlobalTrack[],
Expand All @@ -43,7 +48,7 @@ type StateProps = {|

type Props = {|
...SizeProps,
...ConnectedProps<{||}, StateProps, {||}>,
...ConnectedProps<OwnProps, StateProps, {||}>,
|};

type State = {|
Expand Down Expand Up @@ -85,6 +90,7 @@ class ActiveTabTimelineImpl extends React.PureComponent<Props, State> {
panelLayoutGeneration,
globalTracks,
globalTrackReferences,
innerElementRef,
} = this.props;

return (
Expand All @@ -102,7 +108,7 @@ class ActiveTabTimelineImpl extends React.PureComponent<Props, State> {
initialSelected={this.state.initialSelected}
forceLayoutGeneration={this.state.forceLayoutGeneration}
>
<ol className="timelineThreadList">
<ol className="timelineThreadList" ref={innerElementRef}>
{globalTracks.map((globalTrack, trackIndex) => (
<TimelineActiveTabGlobalTrack
key={trackIndex}
Expand All @@ -119,7 +125,7 @@ class ActiveTabTimelineImpl extends React.PureComponent<Props, State> {
}
}

export const ActiveTabTimeline = explicitConnect<{||}, StateProps, {||}>({
export const ActiveTabTimeline = explicitConnect<OwnProps, StateProps, {||}>({
mapStateToProps: (state) => ({
globalTracks: getActiveTabGlobalTracks(state),
globalTrackReferences: getActiveTabGlobalTrackReferences(state),
Expand Down
15 changes: 13 additions & 2 deletions src/components/timeline/FullTimeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ import type {

import type { ConnectedProps } from 'firefox-profiler/utils/connect';

type OwnProps = {|
// This ref will be added to the inner container.
+innerElementRef?: React.Ref<any>,
|};

type StateProps = {|
+committedRange: StartEndRange,
+globalTracks: GlobalTrack[],
Expand All @@ -64,7 +69,7 @@ type DispatchProps = {|

type Props = {|
...SizeProps,
...ConnectedProps<{||}, StateProps, DispatchProps>,
...ConnectedProps<OwnProps, StateProps, DispatchProps>,
|};

type State = {|
Expand Down Expand Up @@ -144,6 +149,7 @@ class FullTimelineImpl extends React.PureComponent<Props, State> {
panelLayoutGeneration,
hiddenTrackCount,
changeRightClickedTrack,
innerElementRef,
} = this.props;

return (
Expand Down Expand Up @@ -173,6 +179,7 @@ class FullTimelineImpl extends React.PureComponent<Props, State> {
order={globalTrackOrder}
orient="vertical"
onChangeOrder={changeGlobalTrackOrder}
innerElementRef={innerElementRef}
>
{globalTracks.map((globalTrack, trackIndex) => (
<TimelineGlobalTrack
Expand All @@ -191,7 +198,11 @@ class FullTimelineImpl extends React.PureComponent<Props, State> {
}
}

export const FullTimeline = explicitConnect<{||}, StateProps, DispatchProps>({
export const FullTimeline = explicitConnect<
OwnProps,
StateProps,
DispatchProps
>({
mapStateToProps: (state) => ({
globalTracks: getGlobalTracks(state),
globalTrackOrder: getGlobalTrackOrder(state),
Expand Down
42 changes: 25 additions & 17 deletions src/components/timeline/OriginsTimeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ import type { ConnectedProps } from 'firefox-profiler/utils/connect';

import './OriginsTimeline.css';

type OwnProps = {|
// This ref will be added to the inner container.
+innerElementRef?: React.Ref<any>,
|};

type StateProps = {|
+committedRange: StartEndRange,
+panelLayoutGeneration: number,
Expand All @@ -51,7 +56,7 @@ type DispatchProps = {|

type Props = {|
...SizeProps,
...ConnectedProps<{||}, StateProps, DispatchProps>,
...ConnectedProps<OwnProps, StateProps, DispatchProps>,
|};

type State = {|
Expand Down Expand Up @@ -145,6 +150,7 @@ class OriginsTimelineView extends React.PureComponent<Props, State> {
width,
panelLayoutGeneration,
originsTimeline,
innerElementRef,
} = this.props;

return (
Expand All @@ -161,7 +167,7 @@ class OriginsTimelineView extends React.PureComponent<Props, State> {
panelLayoutGeneration={panelLayoutGeneration}
initialSelected={this.state.initialSelected}
>
<ol className="timelineThreadList">
<ol className="timelineThreadList" ref={innerElementRef}>
{originsTimeline.map(this.renderTrack)}
</ol>
</OverflowEdgeIndicator>
Expand All @@ -171,18 +177,20 @@ class OriginsTimelineView extends React.PureComponent<Props, State> {
}
}

export const TimelineOrigins = explicitConnect<{||}, StateProps, DispatchProps>(
{
mapStateToProps: (state) => ({
threads: getThreads(state),
committedRange: getCommittedRange(state),
zeroAt: getZeroAt(state),
panelLayoutGeneration: getPanelLayoutGeneration(state),
originsTimeline: getOriginsTimeline(state),
}),
mapDispatchToProps: {
changeSelectedThreads,
},
component: withSize<Props>(OriginsTimelineView),
}
);
export const TimelineOrigins = explicitConnect<
OwnProps,
StateProps,
DispatchProps
>({
mapStateToProps: (state) => ({
threads: getThreads(state),
committedRange: getCommittedRange(state),
zeroAt: getZeroAt(state),
panelLayoutGeneration: getPanelLayoutGeneration(state),
originsTimeline: getOriginsTimeline(state),
}),
mapDispatchToProps: {
changeSelectedThreads,
},
component: withSize<Props>(OriginsTimelineView),
});
67 changes: 64 additions & 3 deletions src/components/timeline/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,76 @@ type StateProps = {|
type Props = ConnectedProps<{||}, StateProps, {||}>;

class TimelineImpl extends React.PureComponent<Props> {
// This may contain a function that's called whenever we want to remove the
// "wheel" listener.
_removeWheelListener: null | (() => mixed) = null;

// This effectively disable the pinch-to-zoom as well as ctrl+mousewheel
// gestures. Indeed in the timeline it is confusing. In the future we'll want
// to couple this with the preview selection like in the Viewport HOC.
preventPinchToZoom(e: WheelEvent) {
if (e.ctrlKey) {
e.preventDefault();
}
}

// This will be registered as a ref property to the DOM element displaying the
// tracks. We use this solution of registering the wheel event in this ref
// listener because:
// * we can't use React's event handling, because it doesn't allow us to use
// the "passive: false" way of registering the event handler. But we need this
// if we want to be able to prevent the default action of page zooming.
// * we want to be sure to register it whenever the element changes.
_onTimelineMountWithRef = (ref: HTMLElement | null) => {
if (this._removeWheelListener) {
this._removeWheelListener();
this._removeWheelListener = null;
}

if (!ref) {
return;
}

// without pinning to a const variable, Flow isn't sure that we don't change
// the `ref` variable in some of the function calls below, and therefore
// that it won't be null.
const existingRef = ref;

// Disable pinch-to-zoom and ctrl + wheel otherwise, on the timeline.
// Indeed the users are used to this gesture to zoom in in our charts, and
// may use the same gesture elsewhere because of their habits, however this
// doesn't do what they expect and instead zooms in the page, which is distracting.
existingRef.addEventListener('wheel', this.preventPinchToZoom, {
passive: false,
});

this._removeWheelListener = () => {
existingRef.removeEventListener('wheel', this.preventPinchToZoom, {
passive: false,
});
};
};

componentWillUnmount() {
if (this._removeWheelListener) {
this._removeWheelListener();
this._removeWheelListener = null;
}
}

render() {
const { timelineTrackOrganization } = this.props;
switch (timelineTrackOrganization.type) {
case 'full':
return <FullTimeline />;
return <FullTimeline innerElementRef={this._onTimelineMountWithRef} />;
case 'active-tab':
return <ActiveTabTimeline />;
return (
<ActiveTabTimeline innerElementRef={this._onTimelineMountWithRef} />
);
case 'origins':
return <TimelineOrigins />;
return (
<TimelineOrigins innerElementRef={this._onTimelineMountWithRef} />
);
default:
throw assertExhaustiveCheck(
timelineTrackOrganization,
Expand Down