Skip to content

Commit

Permalink
Use generic higher-order component for drag-to-resize vertical divider
Browse files Browse the repository at this point in the history
Applies new `resizableFlex` higher-order component to the `<Workspace>`
component to control resizing of the flex regions containing the editors
column and output respectively. Fully removes the tightly-coupled flex
resizing support from Redux and React infrastructure.

Differences between calculating flex for row and column flex directions
are encapsulated in a pair of adapters.

Flex direction-specific property access now in adapter

Fix adapter usage

Use generic HOC for drag-to-resize editors column and preview

Only works in one direction because of pointer capture

Restore start/stop drag

Fix lint

Remove explicit Redux-level support for drag-to-resize

Removes support for resizing the center vertical divider, now that this
functionality has been migrated to the resizeable flex module.

One dangling rowflex reference
  • Loading branch information
outoftime committed Jun 14, 2018
1 parent f8aa1e4 commit c8a0fbb
Show file tree
Hide file tree
Showing 8 changed files with 62 additions and 239 deletions.
2 changes: 0 additions & 2 deletions src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import {
import {
focusLine,
editorFocusedRequestedLine,
dragColumnDivider,
startDragColumnDivider,
stopDragColumnDivider,
notificationTriggered,
Expand Down Expand Up @@ -87,7 +86,6 @@ export {
toggleComponent,
focusLine,
editorFocusedRequestedLine,
dragColumnDivider,
startDragColumnDivider,
stopDragColumnDivider,
notificationTriggered,
Expand Down
4 changes: 0 additions & 4 deletions src/actions/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ export const editorFocusedRequestedLine = createAction(
'EDITOR_FOCUSED_REQUESTED_LINE',
);

export const dragColumnDivider = createAction(
'DRAG_COLUMN_DIVIDER',
);

export const startDragColumnDivider = createAction(
'START_DRAG_COLUMN_DIVIDER',
);
Expand Down
46 changes: 14 additions & 32 deletions src/components/Workspace.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import {DraggableCore} from 'react-draggable';
import bindAll from 'lodash-es/bindAll';
import isNull from 'lodash-es/isNull';
Expand All @@ -8,7 +9,6 @@ import partial from 'lodash-es/partial';
import {t} from 'i18next';
import classnames from 'classnames';

import {getNodeWidth, getNodeWidths} from '../util/resize';
import {getQueryParameters, setQueryParameters} from '../util/queryParams';
import {dehydrateProject, rehydrateProject} from '../clients/localStorage';

Expand All @@ -29,9 +29,6 @@ export default class Workspace extends React.Component {
this,
'_handleUnload',
'_handleClickInstructionsBar',
'_handleDividerDrag',
'_storeDividerRef',
'_storeColumnRef',
);
this.columnRefs = [null, null];
}
Expand Down Expand Up @@ -104,55 +101,39 @@ export default class Workspace extends React.Component {
);
}

_storeColumnRef(index, column) {
this.columnRefs[index] = column;
}

_storeDividerRef(divider) {
this.dividerRef = divider;
}

_handleDividerDrag(_, {deltaX, lastX, x}) {
const {onDragColumnDivider} = this.props;
onDragColumnDivider({
columnWidths: getNodeWidths(this.columnRefs),
dividerWidth: getNodeWidth(this.dividerRef),
deltaX,
lastX,
x,
});
}

_renderEnvironment() {
const {
currentProject,
resizableFlexGrow,
resizableFlexRefs,
onResizableFlexDividerDrag,
onStartDragColumnDivider,
onStopDragColumnDivider,
rowsFlex,
} = this.props;
if (isNull(currentProject)) {
return <PopThrobber message={t('workspace.loading')} />;
}

const [_handleEditorsRef, _handleOutputRef] = resizableFlexRefs;

return (
<div className="environment">
<EditorsColumn
style={{flex: rowsFlex[0]}}
onRef={partial(this._storeColumnRef, 0)}
style={{flexGrow: resizableFlexGrow.get(0)}}
onRef={_handleEditorsRef}
/>
<DraggableCore
onDrag={this._handleDividerDrag}
onDrag={partial(onResizableFlexDividerDrag, 0)}
onStart={onStartDragColumnDivider}
onStop={onStopDragColumnDivider}
>
<div
className="editors__column-divider"
ref={this._storeDividerRef}
/>
</DraggableCore>
<Output
style={{flex: rowsFlex[1]}}
onRef={partial(this._storeColumnRef, 1)}
style={{flexGrow: resizableFlexGrow.get(1)}}
onRef={_handleOutputRef}
/>
</div>
);
Expand All @@ -178,10 +159,11 @@ export default class Workspace extends React.Component {
Workspace.propTypes = {
currentProject: PropTypes.object,
isEditingInstructions: PropTypes.bool.isRequired,
rowsFlex: PropTypes.array.isRequired,
resizableFlexGrow: ImmutablePropTypes.list.isRequired,
resizableFlexRefs: PropTypes.array.isRequired,
onApplicationLoaded: PropTypes.func.isRequired,
onComponentToggle: PropTypes.func.isRequired,
onDragColumnDivider: PropTypes.func.isRequired,
onResizableFlexDividerDrag: PropTypes.func.isRequired,
onStartDragColumnDivider: PropTypes.func.isRequired,
onStopDragColumnDivider: PropTypes.func.isRequired,
};
Expand Down
13 changes: 4 additions & 9 deletions src/containers/Workspace.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,17 @@ import {connect} from 'react-redux';
import Workspace from '../components/Workspace';
import {getCurrentProject, isEditingInstructions} from '../selectors';
import {
dragColumnDivider,
startDragColumnDivider,
stopDragColumnDivider,
toggleComponent,
applicationLoaded,
startDragColumnDivider,
stopDragColumnDivider,
} from '../actions';
import resizableFlex from '../util/resizableFlex';

function mapStateToProps(state) {
return {
currentProject: getCurrentProject(state),
isEditingInstructions: isEditingInstructions(state),
rowsFlex: state.getIn(['ui', 'workspace', 'rowFlex']).toJS(),
};
}

Expand All @@ -28,10 +27,6 @@ function mapDispatchToProps(dispatch) {
dispatch(toggleComponent(projectKey, componentName));
},

onDragColumnDivider(payload) {
dispatch(dragColumnDivider(payload));
},

onStartDragColumnDivider() {
dispatch(startDragColumnDivider());
},
Expand All @@ -45,4 +40,4 @@ function mapDispatchToProps(dispatch) {
export default connect(
mapStateToProps,
mapDispatchToProps,
)(Workspace);
)(resizableFlex(2)(Workspace));
17 changes: 0 additions & 17 deletions src/reducers/ui.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import Immutable from 'immutable';

import {updateWorkspaceRowFlex} from '../util/resize';

const DEFAULT_ROW_FLEX = new Immutable.List(['1', '1']);
export const DEFAULT_WORKSPACE = new Immutable.Map({
rowFlex: DEFAULT_ROW_FLEX,
isDraggingColumnDivider: false,
isEditingInstructions: false,
});
Expand Down Expand Up @@ -53,13 +49,6 @@ export default function ui(stateIn, action) {
case 'PROJECT_CREATED':
return state.set('workspace', DEFAULT_WORKSPACE);

case 'HIDE_COMPONENT':
case 'UNHIDE_COMPONENT':
if (action.payload.componentName === 'output') {
return state.setIn(['workspace', 'rowFlex'], DEFAULT_ROW_FLEX);
}
return state;

case 'UPDATE_PROJECT_SOURCE':
return state.setIn(['editors', 'typing'], true);

Expand Down Expand Up @@ -87,12 +76,6 @@ export default function ui(stateIn, action) {
case 'EDITOR_FOCUSED_REQUESTED_LINE':
return state.setIn(['editors', 'requestedFocusedLine'], null);

case 'DRAG_COLUMN_DIVIDER':
return state.updateIn(['workspace', 'rowFlex'], (prevFlex) => {
const newFlex = updateWorkspaceRowFlex(action.payload);
return newFlex ? Immutable.fromJS(newFlex) : prevFlex;
});

case 'START_DRAG_COLUMN_DIVIDER':
return state.setIn(['workspace', 'isDraggingColumnDivider'], true);

Expand Down
56 changes: 44 additions & 12 deletions src/util/resizableFlex.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,33 @@ import times from 'lodash-es/times';
import {makeGetResizableFlexGrow} from '../selectors';
import {updateResizableFlex} from '../actions';

const directionAdapters = {
column: {
getCurrentSize(element) {
return element.offsetHeight;
},

getDesiredSize(element, {y}) {
return y - element.offsetTop;
},
},

row: {
getCurrentSize(element) {
return element.offsetWidth;
},

getDesiredSize(element, {x}) {
return x - element.offsetLeft;
},
},
};

function directionAdapterFor({parentNode}) {
const flexDirection = getComputedStyle(parentNode)['flex-direction'];
return directionAdapters[flexDirection];
}

function calculateFlexGrowAfterDrag(
{
currentFlexGrow: currentBeforeFlexGrow,
Expand Down Expand Up @@ -60,21 +87,24 @@ export default function resizableFlex(size) {
const [{current: before}, {current: after}] =
at(regions, [beforeIndex, afterIndex]);

const {getCurrentSize, getDesiredSize} =
directionAdapterFor(before);

const [desiredBeforeFlexGrow, desiredAfterFlexGrow] =
calculateFlexGrowAfterDrag(
{
currentFlexGrow: Number(
getComputedStyle(before)['flex-grow'],
),
currentSize: before.offsetHeight,
desiredSize: payload.y - before.offsetTop,
currentSize: getCurrentSize(before),
desiredSize: getDesiredSize(before, payload),
initialMainSize: initialMainSizes[beforeIndex],
},
{
currentFlexGrow: Number(
getComputedStyle(after)['flex-grow'],
),
currentSize: after.offsetHeight,
currentSize: getCurrentSize(after),
initialMainSize: initialMainSizes[afterIndex],
},
);
Expand All @@ -90,18 +120,20 @@ export default function resizableFlex(size) {

resizableFlexRefs: map(
regions,
(region, index) => (ref) => {
region.current = ref;
if (isNull(ref)) {
(region, index) => (element) => {
region.current = element;
if (isNull(element)) {
initialMainSizes[index] = null;
return;
}
const flexGrowWas = ref.style.flexGrow;
const flexShrinkWas = ref.style.flexShrink;
ref.style.flexGrow = ref.style.flexShrink = '0';
initialMainSizes[index] = ref.offsetHeight;
ref.style.flexGrow = flexGrowWas;
ref.style.flexShrink = flexShrinkWas;

const flexGrowWas = element.style.flexGrow;
const flexShrinkWas = element.style.flexShrink;
element.style.flexGrow = element.style.flexShrink = '0';
initialMainSizes[index] = directionAdapterFor(element).
getCurrentSize(element);
element.style.flexGrow = flexGrowWas;
element.style.flexShrink = flexShrinkWas;
},
),

Expand Down
Loading

0 comments on commit c8a0fbb

Please sign in to comment.