Source: components/FormBuilder/withResizeableColumns.es.js

import Component from 'metal-jsx';
import Position from 'metal-position';
import {Config} from 'metal-state';
import {Drag} from 'metal-drag-drop';
import {focusedFieldStructure, pageStructure, ruleStructure} from '../../util/config.es';

const withResizeableColumns = ChildComponent => {
	class ResizeableColumns extends Component {
		static PROPS = {

			/**
			 * @default
			 * @instance
			 * @memberof FormBuilder
			 * @type {?number}
			 */

			activePage: Config.number().value(0),

			/**
			 * @default undefined
			 * @instance
			 * @memberof FormBuilder
			 * @type {?string}
			 */

			defaultLanguageId: Config.string(),

			/**
			 * @default undefined
			 * @instance
			 * @memberof FormBuilder
			 * @type {?string}
			 */

			editingLanguageId: Config.string(),

			/**
			 * @default []
			 * @instance
			 * @memberof Sidebar
			 * @type {?(array|undefined)}
			 */

			fieldTypes: Config.array().value([]),

			/**
			 * @default {}
			 * @instance
			 * @memberof FormBuilder
			 * @type {?object}
			 */

			focusedField: focusedFieldStructure.value({}),

			/**
			 * @default []
			 * @instance
			 * @memberof FormBuilder
			 * @type {?array<object>}
			 */

			pages: Config.arrayOf(pageStructure).value([]),

			/**
			 * @instance
			 * @memberof FormBuilder
			 * @type {string}
			 */

			paginationMode: Config.string().required(),

			/**
			 * @instance
			 * @memberof FormBuilder
			 * @type {string}
			 */

			rules: Config.arrayOf(ruleStructure).required(),

			/**
			 * @default undefined
			 * @instance
			 * @memberof FormRenderer
			 * @type {!string}
			 */

			spritemap: Config.string().required()
		}

		attached() {
			this._createResizeDrag();
		}

		disposeInternal() {
			super.disposeInternal();

			if (this._resizeDrag) {
				this._resizeDrag.dispose();
			}
		}

		isResizeEnabled() {
			const {defaultLanguageId, editingLanguageId} = this.props;

			return defaultLanguageId === editingLanguageId;
		}

		render() {
			return (
				<div class={this.isResizeEnabled() ? 'resizeable' : ''}>
					{this.renderResizeReferences()}

					<ChildComponent {...this.props} />
				</div>
			);
		}

		renderResizeReferences() {
			return [...Array(12)].map(
				(element, index) => {
					return (
						<div
							class="ddm-resize-column"
							data-resize-column={index}
							key={index}
							ref={`resizeColumn${index}`}
						/>
					);
				}
			);
		}

		_createResizeDrag() {
			this._resizeDrag = new Drag(
				{
					axis: 'x',
					sources: '.resizeable .ddm-resize-handle',
					useShim: true
				}
			);

			this._resizeDrag.on(Drag.Events.START, this._handleResizeDragStartEvent.bind(this));
			this._resizeDrag.on(Drag.Events.DRAG, this._handleResizeDragEvent.bind(this));
		}

		_handleResizeDragEvent(event) {
			const columnNodes = Object.keys(this.refs)
				.filter(key => key.indexOf('resizeColumn') === 0)
				.map(key => this.refs[key]);
			const {source, x} = event;
			const {store} = this.context;

			let distance = Infinity;
			let nearest;

			columnNodes.forEach(
				node => {
					const region = Position.getRegion(node);

					const currentDistance = Math.abs(x - region.left);

					if (currentDistance < distance) {
						distance = currentDistance;
						nearest = node;
					}
				}
			);

			if (nearest) {
				const column = Number(nearest.dataset.resizeColumn);
				const direction = source.classList.contains('ddm-resize-handle-left') ? 'left' : 'right';

				if (this._lastResizeColumn !== column) {
					this._lastResizeColumn = column;

					store.emit(
						'columnResized',
						{
							column,
							direction,
							source
						}
					);
				}
			}
		}

		_handleResizeDragStartEvent() {
			this._lastResizeColumn = -1;
		}
	}

	return ResizeableColumns;
};

export default withResizeableColumns;