Source: components/FormBuilder/withResizeableColumns.es.js

/**
 * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 */

import dom from 'metal-dom';
import {Drag} from 'metal-drag-drop';
import Component from 'metal-jsx';
import Position from 'metal-position';
import {Config} from 'metal-state';

import {focusedFieldStructure, pageStructure} from '../../util/config.es';

const withResizeableColumns = ChildComponent => {
	class ResizeableColumns extends Component {
		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.END,
				this._handleDragEnd.bind(this)
			);

			this._resizeDrag.on(
				Drag.Events.DRAG,
				this._handleDragMove.bind(this)
			);

			this._resizeDrag.on(
				Drag.Events.START,
				this._handleDragStart.bind(this)
			);
		}

		_handleDragEnd({source}) {
			const {parentElement} = source;

			if (parentElement) {
				parentElement.classList.remove('dragging');
			}
		}

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

		_handleDragMove(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;

			const container = dom.closest(source, '.ddm-field-container');

			if (container) {
				container.classList.add('dragging');
			}

			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,
					});
				}
			}
		}
	}

	ResizeableColumns.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 undefined
		 * @instance
		 * @memberof FormBuilder
		 * @type {?string}
		 */

		fieldSetDefinitionURL: Config.string(),

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

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

		/**
		 * @default []
		 * @instance
		 * @memberof FormBuilder
		 * @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}
		 */

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

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

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

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

		successPageSettings: Config.shapeOf({
			body: Config.object(),
			enabled: Config.bool(),
			title: Config.object(),
		}).value({}),

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

		view: Config.string(),
	};

	return ResizeableColumns;
};

export default withResizeableColumns;