react-layouts-builder
Version:
Lightweight and powerfull react layouts drag and drop
597 lines (492 loc) • 19.6 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var React = require('react');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
function __spreadArray(to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
}
var LayoutContext = /*#__PURE__*/React.createContext({});
var LayoutProvider = function LayoutProvider(_a) {
var children = _a.children;
var _b = React.useState(),
dataItem = _b[0],
setDataItem = _b[1];
var context = React.useMemo(function () {
return {
dataItem: dataItem,
setDataItem: setDataItem
};
}, [dataItem]);
return /*#__PURE__*/React__default["default"].createElement(LayoutContext.Provider, {
value: context
}, children);
};
var DragNDrop = function DragNDrop(_a) {
var children = _a.children,
layout = _a.layout,
handleDrop = _a.handleDrop;
var _b = React.useState(false),
isDragging = _b[0],
setIsDragging = _b[1];
var _c = React.useState(),
dragOverPosition = _c[0],
setDragOverPosition = _c[1];
var _d = React.useContext(LayoutContext),
dataItem = _d.dataItem,
setDataItem = _d.setDataItem;
var _e = React.useState(),
mouseOver = _e[0],
setMouseOver = _e[1];
var ref = React.useRef(null);
var handleDragStart = function handleDragStart(event) {
setDataItem(layout);
console.log('This run setters');
changeEditable(false);
};
var layoutId = layout['@id'] || layout.id;
var handleDragOver = function handleDragOver(event) {
event.preventDefault();
event.stopPropagation();
if (!dataItem) {
return;
}
setIsDragging(true);
if (!ref.current) return;
var _a = ref.current,
offsetLeft = _a.offsetLeft,
offsetTop = _a.offsetTop; // Calculate the height and width of the target element
var height = ref.current.offsetHeight;
ref.current.offsetWidth; // Calculate the vertical and horizontal thresholds for positioning
var verticalThreshold = height / 2;
// Calculate the cursor position relative to the target element
var cursorY = event.clientY - offsetTop;
event.clientX - offsetLeft; // Determine the position based on the cursor position and thresholds
// if (cursorX < horizontalThreshold) {
// setDragOverPosition('left');
// return;
// } else if (cursorX > width - horizontalThreshold) {
// setDragOverPosition('right');
// return;
// }
if (cursorY < verticalThreshold) {
setDragOverPosition('top');
return;
} else {
setDragOverPosition('bottom');
return;
}
};
var handleDropEvent = function handleDropEvent(event) {
event.preventDefault();
var position = dragOverPosition;
changeEditable(true);
if (dataItem && position) {
event.dataTransfer.clearData();
handleDrop({
item: dataItem,
targetItemId: layoutId,
position: position
});
} // Clear the dragged item state
setDataItem(undefined);
handleDragLeave();
};
var handleDragLeave = function handleDragLeave() {
setIsDragging(false);
setDragOverPosition(undefined);
};
var changeEditable = function changeEditable(b) {
var editableElements = document.querySelectorAll('.your-class-name');
editableElements.forEach(function (element) {
element.setAttribute('contenteditable', !b ? 'false' : 'true');
});
};
var attrs = layout.block ? {
draggable: true,
onDragStart: handleDragStart,
className: "droppable-container ".concat(isDragging ? "lb-indice drop-".concat(dragOverPosition) : '', " ")
} : {};
return /*#__PURE__*/React__default["default"].createElement("div", __assign({
ref: ref,
onDragOver: handleDragOver,
onDrop: handleDropEvent,
onDragLeave: handleDragLeave
}, attrs), /*#__PURE__*/React__default["default"].createElement("div", {
className: "border-hidden ".concat(mouseOver === layoutId ? 'border-gray-200' : ''),
onMouseOver: function onMouseOver(e) {
e.preventDefault();
e.stopPropagation();
setMouseOver(layoutId);
},
onMouseLeave: function onMouseLeave() {
return setMouseOver(undefined);
}
}, /*#__PURE__*/React__default["default"].createElement("div", {
className: ""
}, children)));
};
var LayoutRecursive = /*#__PURE__*/React.memo(function (_a) {
var _b;
var layouts = _a.layouts,
_c = _a.level,
level = _c === void 0 ? 1 : _c,
onDrop = _a.onDrop,
renderBlock = _a.renderBlock;
var hasChildren = layouts.childrens && layouts.childrens.length > 0;
var isCol = layouts.display && layouts.display.orientation === 'col';
return /*#__PURE__*/React__default["default"].createElement("div", {
className: "".concat(hasChildren && !isCol ? 'lb-spacing' : '', " ").concat(isCol ? 'lb-col' : 'lb-w-full'),
"data-key": layouts['@id'] || layouts.id
}, /*#__PURE__*/React__default["default"].createElement(DragNDrop, {
layout: layouts,
handleDrop: onDrop,
key: layouts['@id'] || layouts.id
}, /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, hasChildren ? (_b = layouts === null || layouts === void 0 ? void 0 : layouts.childrens) === null || _b === void 0 ? void 0 : _b.map(function (container) {
return /*#__PURE__*/React__default["default"].createElement(LayoutRecursive, {
layouts: container,
onDrop: onDrop,
renderBlock: renderBlock,
key: container['@id'] || container.id,
level: level + 1,
parentId: layouts['@id'] || layouts.id
});
}) : /*#__PURE__*/React__default["default"].createElement("div", {
className: "lb-block"
}, layouts.block ? renderBlock(layouts.block) : null))));
});
function insertLayout$1(options) {
var layouts = options.layouts,
targetId = options.targetId,
moved = options.moved,
position = options.position;
function findParentAndInsert(layout) {
var _a, _b, _c;
var targetLayout = (_a = layout === null || layout === void 0 ? void 0 : layout.childrens) === null || _a === void 0 ? void 0 : _a.find(function (child) {
var id = child['@id'] || child.id;
return id === targetId;
});
if (targetLayout && ((_b = layout.childrens) === null || _b === void 0 ? void 0 : _b.length)) {
var childrens = layout.childrens.reduce(function (childs, nextChild) {
var id = nextChild['@id'] || nextChild.id;
if (id === targetId) {
if (position === 'top') {
return __spreadArray(__spreadArray([], childs, true), [moved, nextChild], false);
}
return __spreadArray(__spreadArray([], childs, true), [nextChild, moved], false);
}
return childs.concat(nextChild);
}, []);
return __assign(__assign({}, layout), {
childrens: childrens
});
} else if ((layout === null || layout === void 0 ? void 0 : layout.childrens) && ((_c = layout.childrens) === null || _c === void 0 ? void 0 : _c.length)) {
var newLayout = layout.childrens.map(function (child) {
return findParentAndInsert(child);
}).filter(function (child) {
return child !== null;
});
return __assign(__assign({}, layout), {
childrens: newLayout
});
}
return layout;
}
return findParentAndInsert(layouts);
}
function removeLayout(layouts, id) {
if (layouts.childrens) {
for (var i = 0; i < layouts.childrens.length; i++) {
var child = layouts.childrens[i];
var childID = child['@id'] || child.id;
if (childID === id) {
layouts.childrens.splice(i, 1);
return layouts;
} else if (child.childrens) {
var removed = removeLayout(child, id);
if (removed) {
if (removed.childrens && removed.childrens.length === 0) {
delete removed.childrens;
}
return layouts;
}
}
}
}
return null;
}
var moveBlock = function moveBlock(data, options) {
var item = options.item,
targetItemId = options.targetItemId,
position = options.position;
var itemId = item['@id'] || item.id;
if (!itemId || !options.targetItemId) return data;
var copyLayout = JSON.parse(JSON.stringify(data));
var removedItem = removeLayout(copyLayout, itemId);
var addToTarget = insertLayout$1({
layouts: removedItem,
moved: item,
targetId: targetItemId,
position: position
});
return removeEmptyChildren(addToTarget);
};
function removeEmptyChildren(layout) {
var updatedChildrens = ((layout === null || layout === void 0 ? void 0 : layout.childrens) || []).map(function (child) {
return removeEmptyChildren(child);
}).filter(function (child) {
return child;
});
if (updatedChildrens.length === 0 && !(layout === null || layout === void 0 ? void 0 : layout.block)) {
return null;
}
var updatedLayout = __assign(__assign({}, layout), {
childrens: updatedChildrens.length > 0 ? updatedChildrens : undefined
});
if (updatedChildrens.length === 0 && !(updatedLayout === null || updatedLayout === void 0 ? void 0 : updatedLayout.block)) {
return null;
}
return updatedLayout;
}
var LayoutBuilder = function LayoutBuilder(_a) {
var layouts = _a.layouts,
renderComponent = _a.renderComponent,
onLayoutChange = _a.onLayoutChange;
var _b = React.useState(layouts),
internalLayouts = _b[0],
setInternalLayouts = _b[1];
var handleDrop = React.useCallback(function (options) {
var itemId = options.item['@id'] || options.item.id;
if (itemId === options.targetItemId) return;
setInternalLayouts(function (prev) {
var newLayouts = moveBlock(prev, options);
if (newLayouts) {
onLayoutChange(newLayouts);
return newLayouts;
}
return prev;
});
}, []);
React.useEffect(function () {
var jsonNEw = JSON.stringify(layouts);
var local = JSON.stringify(internalLayouts);
if (jsonNEw !== local) {
setInternalLayouts(__assign({}, layouts));
}
}, [layouts]);
return /*#__PURE__*/React__default["default"].createElement(LayoutProvider, null, /*#__PURE__*/React__default["default"].createElement("div", null, internalLayouts['@id'] || internalLayouts.id ? /*#__PURE__*/React__default["default"].createElement(LayoutRecursive, {
layouts: internalLayouts,
onDrop: handleDrop,
renderBlock: renderComponent
}) : null));
};
exports.EnumBlockType = void 0;
(function (EnumBlockType) {
EnumBlockType["CONTAINER"] = "container";
EnumBlockType["CHILDREN"] = "row";
EnumBlockType["BLOCK"] = "block";
})(exports.EnumBlockType || (exports.EnumBlockType = {}));
exports.EnumPosition = void 0;
(function (EnumPosition) {
EnumPosition["TOP"] = "top";
EnumPosition["BOTTOM"] = "bottom";
EnumPosition["LEFT"] = "left";
EnumPosition["RIGHT"] = "right";
})(exports.EnumPosition || (exports.EnumPosition = {}));
// Unique ID creation requires a high quality random # generator. In the browser we therefore
// require the crypto API and do not support built-in fallback to lower quality random number
// generators (like Math.random()).
var getRandomValues;
var rnds8 = new Uint8Array(16);
function rng() {
// lazy load so that environments that need to polyfill have a chance to do so
if (!getRandomValues) {
// getRandomValues needs to be invoked in a context where "this" is a Crypto implementation. Also,
// find the complete implementation of crypto (msCrypto) on IE11.
getRandomValues = typeof crypto !== 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto) || typeof msCrypto !== 'undefined' && typeof msCrypto.getRandomValues === 'function' && msCrypto.getRandomValues.bind(msCrypto);
if (!getRandomValues) {
throw new Error('crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported');
}
}
return getRandomValues(rnds8);
}
var REGEX = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;
function validate(uuid) {
return typeof uuid === 'string' && REGEX.test(uuid);
}
/**
* Convert array of 16 byte values to UUID string format of the form:
* XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
*/
var byteToHex = [];
for (var i = 0; i < 256; ++i) {
byteToHex.push((i + 0x100).toString(16).substr(1));
}
function stringify(arr) {
var offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
// Note: Be careful editing this code! It's been tuned for performance
// and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434
var uuid = (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); // Consistency check for valid UUID. If this throws, it's likely due to one
// of the following:
// - One or more input array values don't map to a hex octet (leading to
// "undefined" in the uuid)
// - Invalid input values for the RFC `version` or `variant` fields
if (!validate(uuid)) {
throw TypeError('Stringified UUID is invalid');
}
return uuid;
}
function v4(options, buf, offset) {
options = options || {};
var rnds = options.random || (options.rng || rng)(); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
rnds[6] = rnds[6] & 0x0f | 0x40;
rnds[8] = rnds[8] & 0x3f | 0x80; // Copy bytes to buffer, if provided
if (buf) {
offset = offset || 0;
for (var i = 0; i < 16; ++i) {
buf[offset + i] = rnds[i];
}
return buf;
}
return stringify(rnds);
}
var createLayoutContainer = function createLayoutContainer(block) {
return {
id: "".concat(v4()),
type: 'Layout',
tag: 'div',
display: {
name: 'mobile',
orientation: '',
order: 0
},
childrens: [{
id: "".concat(v4()),
type: 'Layout',
tag: 'div',
block: block
}]
};
};
var createLayoutBlock = function createLayoutBlock(block) {
return {
id: "".concat(v4()),
type: 'Layout',
tag: 'div',
display: {
name: 'mobile',
orientation: '',
order: 0
},
block: block
};
};
var createContainer = function createContainer(options) {
if (!options) {
throw new Error('createContainer(options: CreateContainerOptions): No options was found');
}
if (!options.layouts || !options.block) {
throw new Error('createContainer(options: CreateContainerOptions): Missing options: layouts and block are required');
}
var layouts = options.layouts,
block = options.block;
var container = createLayoutContainer(block);
return __assign(__assign({}, layouts), {
childrens: __spreadArray(__spreadArray([], layouts.childrens || [], true), [container], false)
});
};
function insertLayout(options) {
var layouts = options.layouts,
targetBlockId = options.targetBlockId,
block = options.block;
function findParentAndInsert(layout) {
var _a, _b, _c;
var targetLayout = (_a = layout.childrens) === null || _a === void 0 ? void 0 : _a.find(function (child) {
var _a, _b;
var blockId = ((_a = child.block) === null || _a === void 0 ? void 0 : _a['@id']) || ((_b = child.block) === null || _b === void 0 ? void 0 : _b.id);
return child.block && blockId === targetBlockId;
});
if (targetLayout && ((_b = layout.childrens) === null || _b === void 0 ? void 0 : _b.length)) {
var childrens = layout.childrens.reduce(function (childs, nextChild) {
var _a, _b;
var nextBlockId = ((_a = nextChild.block) === null || _a === void 0 ? void 0 : _a['@id']) || ((_b = nextChild.block) === null || _b === void 0 ? void 0 : _b.id);
if (nextChild.block && nextBlockId === targetBlockId) {
return __spreadArray(__spreadArray([], childs, true), [nextChild, createLayoutBlock(block)], false);
}
return childs.concat(nextChild);
}, []);
return __assign(__assign({}, layout), {
childrens: childrens
});
} else if (layout.childrens && ((_c = layout.childrens) === null || _c === void 0 ? void 0 : _c.length)) {
var newLayout = layout.childrens.map(function (child) {
return findParentAndInsert(child);
}).filter(function (child) {
return child !== null;
});
return __assign(__assign({}, layout), {
childrens: newLayout
});
}
return layout;
}
return findParentAndInsert(layouts);
}
function removeBlockFromLayout(layouts, id) {
var _a, _b;
if (layouts.childrens) {
for (var i = 0; i < layouts.childrens.length; i++) {
var child = layouts.childrens[i];
var blockId = ((_a = child.block) === null || _a === void 0 ? void 0 : _a['@id']) || ((_b = child.block) === null || _b === void 0 ? void 0 : _b.id);
if (blockId === id) {
layouts.childrens.splice(i, 1);
return layouts;
} else if (child.childrens) {
var removed = removeBlockFromLayout(child, id);
if (removed) {
if (removed.childrens && removed.childrens.length === 0) {
delete removed.childrens;
}
return layouts;
}
}
}
}
return null;
}
var removeBlock = function removeBlock(layouts, blockId) {
var layout = removeBlockFromLayout(layouts, blockId);
return removeEmptyChildren(layout);
};
exports.LayoutBuilder = LayoutBuilder;
exports.createContainer = createContainer;
exports.insertLayout = insertLayout;
exports.removeBlock = removeBlock;