61 |
62 |
63 | { this.props.children }
64 |
65 |
66 | );
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/example/src/widgets/RichTextRenderer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | //import backdraft from 'backdraft-js';
3 | import {convertToRaw,convertFromRaw} from 'draft-js';
4 | import {stateToHTML} from 'draft-js-export-html';
5 | import styleFont from './utils/font';
6 |
7 | // const markup = {
8 | // 'BOLD': ['
', ''],
9 | // 'ITALIC': ['
', ''],
10 | // 'UNDERLINE': ['
', ''],
11 | // 'CODE': ['
', '']
12 | // }
13 |
14 | let RichTextRenderer = (props) => {
15 |
16 | var style = props.style || {};
17 |
18 | styleFont(style, props.font);
19 |
20 | //size
21 | if (props.height) style.height = props.height;
22 | if (props.width) style.width = props.width;
23 |
24 | var htmlContent = props.content !== undefined ? stateToHTML(convertFromRaw(props.content)) : ['type your content'];
25 |
26 | return (
27 |
28 |
29 |
30 | );
31 | }
32 | //RichEditorRenderer.defaultProps = {content:'type your content'};
33 | export default RichTextRenderer;
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/example/src/widgets/TextRenderer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import styleFont from './utils/font';
4 |
5 | let TextRenderer = (props) => {
6 |
7 | var style = props.style || {};
8 |
9 | styleFont(style, props.font);
10 |
11 | //size
12 | if (props.height) style.height = props.height;
13 | if (props.width) style.width = props.width;
14 |
15 |
16 | return (
17 |
{props.content}
18 | );
19 | }
20 |
21 | TextRenderer.defaultProps = {
22 | content:'type your content'
23 | };
24 | export default TextRenderer;
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/example/src/widgets/utils/backgroundStyle.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 |
3 | export default function(source,panelSize) {
4 | var bg = source;
5 | var bgStyle = {};
6 | if (bg === undefined) return bgStyle;
7 |
8 | //size
9 | if (!!bg.size) {
10 | if (panelSize !== undefined && (bg.size === "leftHalf" || bg.size === "rightHalf")) {
11 | bgStyle.backgroundSize = `${panelSize.width * 2}px ${panelSize.height}px`;
12 | bgStyle.backgroundPosition = bg.size === "leftHalf" ? '0% 0%' : '100% 0%';
13 | //console.log(bgStyle);
14 | }
15 | else{
16 | bgStyle.backgroundSize = bg.size;
17 | if (!!bg.position) bgStyle.backgroundPosition = bg.position;
18 | }
19 | }
20 | //gradient
21 | var bgGradient = bg.gradient;
22 | if (bgGradient !== undefined) {
23 | //gradient
24 | if (bgGradient.stops !== undefined) {
25 | var gradientStops = _.reduce(bgGradient.stops, function (memo, stop) {
26 | return memo + ', ' + stop.color + ' ' + (stop.offset * 100) + '%'
27 | }, '');
28 |
29 | var orientation = bgGradient.orientation || 'top';
30 | var grandientType = bgGradient.orientation === 'center, ellipse cover' ? '-webkit-radial-gradient' : '-webkit-linear-gradient';
31 | bgStyle.background = grandientType + '(' + orientation + gradientStops + ')';
32 | }
33 | }
34 |
35 | //color
36 | var bgColor = bg.color;
37 | if (bgColor !== undefined) {
38 | if (!!bgColor.color) bgStyle.backgroundColor = bgColor.color;
39 | if (!!bgColor.alpha) bgStyle.opacity = bgColor.alpha / 100;
40 | }
41 |
42 | if (!!bg.image) bgStyle.backgroundImage = 'url(' + bg.image + ')';
43 | if (!!bg.repeat) bgStyle.backgroundRepeat = bg.repeat;
44 | if (!!bg.attachment) bgStyle.backgroundAttachment = bg.attachment;
45 |
46 | var filter = bg.filter || {};
47 | var cssFilter = "";
48 | if (!!filter.blur) cssFilter += ' blur(' + filter.blur + 'px)';
49 | if (!!filter.brightness) cssFilter += ' brightness(' + filter.brightness + '%)';
50 | if (!!filter.contrast) cssFilter += ' contrast(' + filter.contrast + '%)';
51 | if (!!filter.grayscale) cssFilter += ' grayscale(' + filter.grayscale + '%)';
52 | if (!!filter.hueRotate) cssFilter += ' hue-rotate(' + filter.hueRotate + 'deg)';
53 | if (!!filter.invert) cssFilter += ' invert(' + filter.invert + '%)';
54 | if (!!filter.opacity) cssFilter += ' opacity(' + filter.opacity + '%)';
55 | if (!!filter.saturate) cssFilter += ' saturate(' + filter.saturate + '%)';
56 | if (!!filter.sepia) cssFilter += ' sepia(' + filter.sepia + '%)';
57 |
58 | if (!!cssFilter) {
59 | bgStyle.WebkitFilter = cssFilter;
60 | bgStyle.filter = cssFilter;
61 | }
62 | //bgStyle.position = 'absolute';
63 | return bgStyle;
64 | }
65 |
--------------------------------------------------------------------------------
/example/src/widgets/utils/border.js:
--------------------------------------------------------------------------------
1 | export default function styleBorder(style, border) {
2 |
3 | if (style === undefined) style = {};
4 | if (border === undefined) return style;
5 |
6 | if (border.width) style.borderWidth = border.width;
7 | if (border.radius) style.borderRadius = border.radius;
8 | if (border.color) style.borderColor = border.color && border.color.color;
9 | style.borderStyle = border.style || 'solid';
10 | return style;
11 | }
12 |
13 |
--------------------------------------------------------------------------------
/example/src/widgets/utils/font.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 |
3 | export default function styleFont(style, fontProps) {
4 |
5 | if (style === undefined) style = {};
6 | if (fontProps === undefined) return style;
7 |
8 | style = _.extend(style,fontProps) || {};
9 | if (fontProps.color) style['color'] = fontProps.color.color;
10 | if (fontProps.bold) style['fontWeight'] = 'bold';
11 | if (fontProps.italic) style['fontStyle'] = 'italic';
12 | if (fontProps.underline) style['borderBottom'] = '1px dashed #999';
13 | return style;
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var initGulpTasks = require('react-component-gulp-tasks');
3 |
4 | /**
5 | * Tasks are added by the react-component-gulp-tasks package
6 | *
7 | * See https://github.com/JedWatson/react-component-gulp-tasks
8 | * for documentation.
9 | *
10 | * You can also add your own additional gulp tasks if you like.
11 | */
12 |
13 | var taskConfig = {
14 |
15 | component: {
16 | name: 'Designer',
17 | dependencies: [
18 | 'classnames',
19 | 'react',
20 | 'react-dom'
21 | ],
22 | lib: 'lib'
23 | },
24 |
25 | example: {
26 | src: 'example/src',
27 | dist: 'example/dist',
28 | port: 8002,
29 | files: [
30 | 'index.html',
31 | 'grid.png',
32 | '.gitignore'
33 | ],
34 | scripts: [
35 | 'example.js'
36 | ],
37 | less: [
38 | 'example.less'
39 | ]
40 | }
41 |
42 | };
43 |
44 | initGulpTasks(gulp, taskConfig);
45 |
--------------------------------------------------------------------------------
/lib/Designer.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, '__esModule', {
4 | value: true
5 | });
6 |
7 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
8 |
9 | var _componentsWorkplaceJs = require('./components/Workplace.js');
10 |
11 | var _componentsWorkplaceJs2 = _interopRequireDefault(_componentsWorkplaceJs);
12 |
13 | var _componentsObjectBrowserJs = require('./components/ObjectBrowser.js');
14 |
15 | var _componentsObjectBrowserJs2 = _interopRequireDefault(_componentsObjectBrowserJs);
16 |
17 | exports['default'] = {
18 | Workplace: _componentsWorkplaceJs2['default'],
19 | ObjectBrowser: _componentsObjectBrowserJs2['default']
20 | };
21 | module.exports = exports['default'];
--------------------------------------------------------------------------------
/lib/components/ObjectBrowser.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, '__esModule', {
4 | value: true
5 | });
6 |
7 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
8 |
9 | var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
10 |
11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
12 |
13 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
14 |
15 | function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
16 |
17 | var _react = require('react');
18 |
19 | var _react2 = _interopRequireDefault(_react);
20 |
21 | var _reactTreeview = require('react-treeview');
22 |
23 | var _reactTreeview2 = _interopRequireDefault(_reactTreeview);
24 |
25 | var _lodash = require('lodash');
26 |
27 | var _lodash2 = _interopRequireDefault(_lodash);
28 |
29 | var _classnames = require('classnames');
30 |
31 | var _classnames2 = _interopRequireDefault(_classnames);
32 |
33 | var CONTAINER_KEYS = ["ObjectSchema", "Container", "Repeater", "Grid", "Cell", "BackgroundContainer"];
34 |
35 | var ObjectBrowser = (function (_React$Component) {
36 | _inherits(ObjectBrowser, _React$Component);
37 |
38 | function ObjectBrowser(props) {
39 | _classCallCheck(this, ObjectBrowser);
40 |
41 | _get(Object.getPrototypeOf(ObjectBrowser.prototype), 'constructor', this).call(this, props);
42 | this.state = { filterText: '' };
43 | }
44 |
45 | _createClass(ObjectBrowser, [{
46 | key: 'handleUserInput',
47 | value: function handleUserInput(e) {
48 | this.setState({
49 | filterText: e.target.value
50 | });
51 | }
52 | }, {
53 | key: 'executeAction',
54 | value: function executeAction(action, args) {
55 | if (action === "onDragStart") {
56 | this.currentItem = args;
57 | return;
58 | }
59 | if (action === "onDrop") {
60 | this.move(this.currentItem, args);
61 | this.currentItem = undefined;
62 | return;
63 | }
64 | }
65 | }, {
66 | key: 'move',
67 | value: function move(from, to) {
68 | //console.log(from.node.name + " -> " + to.node.name);
69 | // transact returns a mutable object
70 | // to make all the local changes
71 |
72 | //find source
73 | var source = from.node;
74 | var isContainer = _lodash2['default'].includes(CONTAINER_KEYS, source.elementName);
75 |
76 | //move source to target - do it in transaction
77 | var targetArray = isContainer ? to.node.containers : to.node.boxes;
78 | targetArray.transact().push(from.node);
79 |
80 | //remove source - do it in transaction
81 | var sourceParent = isContainer ? from.parentNode.containers : from.parentNode.boxes;
82 | var indexToRemove = sourceParent.indexOf(source);
83 | //console.log(indexToRemove);
84 | if (indexToRemove !== -1) {
85 | sourceParent.transact().splice(indexToRemove, 1);
86 | }
87 |
88 | // all the changes are made at once
89 | targetArray.run();
90 | sourceParent.run();
91 |
92 | // use it as a normal array
93 | //trans[0] = 1000; // [1000, 1, 2, ..., 999]
94 | }
95 | }, {
96 | key: 'render',
97 | value: function render() {
98 | var _this = this;
99 |
100 | var classes = (0, _classnames2['default'])({
101 | 'node': true,
102 | 'selected': this.props.current.node === this.props.rootNode
103 | });
104 | var path = 'schema';
105 | return _react2['default'].createElement(
106 | 'div',
107 | null,
108 | _react2['default'].createElement(
109 | 'div',
110 | { className: 'form-group' },
111 | _react2['default'].createElement('input', { type: 'search', className: 'form-control', placeholder: 'Search for...', onChange: this.handleUserInput.bind(this) })
112 | ),
113 | _react2['default'].createElement(
114 | 'div',
115 | { className: classes, onClick: function (e) {
116 | return _this.props.currentChanged(_this.props.rootNode, path);
117 | } },
118 | this.props.rootNode.name
119 | ),
120 | this.props.rootNode.containers.length === 0 ? _react2['default'].createElement(
121 | 'span',
122 | null,
123 | 'No objects to show.'
124 | ) : _react2['default'].createElement(TreeNode, { key: 'root', path: path, node: this.props.rootNode, current: this.props.current,
125 | currentChanged: this.props.currentChanged.bind(this), filterText: this.state.filterText,
126 | executeAction: this.executeAction.bind(this) })
127 | );
128 | }
129 | }]);
130 |
131 | return ObjectBrowser;
132 | })(_react2['default'].Component);
133 |
134 | exports['default'] = ObjectBrowser;
135 | ;
136 |
137 | var TreeNode = (function (_React$Component2) {
138 | _inherits(TreeNode, _React$Component2);
139 |
140 | function TreeNode() {
141 | _classCallCheck(this, TreeNode);
142 |
143 | _get(Object.getPrototypeOf(TreeNode.prototype), 'constructor', this).apply(this, arguments);
144 | }
145 |
146 | _createClass(TreeNode, [{
147 | key: 'handleClick',
148 | value: function handleClick(node, path) {
149 | this.props.currentChanged(node, path);
150 | }
151 |
152 | //TODO: optimize -> now each node starts its own tree traversal
153 | }, {
154 | key: 'hideNode',
155 | value: function hideNode(node, filterText) {
156 | var trav = function trav(node) {
157 | var containers = node.containers;
158 | var boxes = node.boxes;
159 | var anyBoxes = _lodash2['default'].some(boxes, function (item) {
160 | return item.name.toLowerCase().indexOf(filterText.toLowerCase()) !== -1;
161 | });
162 | if (anyBoxes) return true;
163 | if (node.name.indexOf(filterText) !== -1) return true;
164 | //recursion condtion stop
165 | var childrenBoxes = false;
166 | for (var i in containers) {
167 | //recursion step
168 | childrenBoxes = trav(containers[i]);
169 | if (childrenBoxes) return true;
170 | }
171 | return false;
172 | };
173 |
174 | return !trav(node);
175 | }
176 | }, {
177 | key: 'shouldComponentUpdate',
178 | value: function shouldComponentUpdate(nextProps) {
179 | return true;
180 | // The comparison is fast, and we won't render the component if
181 | // it does not need it. This is a huge gain in performance.
182 | //var current = this.props.current.node;
183 | //var nextCurrent = nextProps.current.node;
184 | //return this.props.filterText != nextProps.filterText || this.props.nodes != nextProps.nodes || (current!==undefined && nextCurrent !==undefined && current.name != nextCurrent.name);
185 | }
186 | }, {
187 | key: 'render',
188 | value: function render() {
189 | var containers = this.props.node.containers || [];
190 |
191 | return _react2['default'].createElement(
192 | 'div',
193 | null,
194 | containers.map(function (node, i) {
195 | if (this.hideNode(node, this.props.filterText)) return;
196 |
197 | var onDragStart = (function (e) {
198 | console.log('drag started');
199 | e.stopPropagation();
200 | var draggingItem = {
201 | node: node,
202 | parentNode: this.props.node };
203 | this.props.executeAction("onDragStart", draggingItem);
204 | }).bind(this);
205 | var onDragEnter = (function (e) {
206 | e.preventDefault(); // Necessary. Allows us to drop.
207 | e.stopPropagation();
208 | //window.event.returnValue=false;
209 | //if (this.props.dragging.type !== this.props.item.type || this.props.dragging.id !== this.props.item.id) {
210 | var dropCandidate = { node: node, parentNode: this.props.node };
211 | // var self = this;
212 | this.props.executeAction("dropPossible", dropCandidate);
213 | //}
214 | }).bind(this);
215 | var onDragOver = (function (e) {
216 | e.preventDefault(); // Necessary. Allows us to drop.
217 | e.stopPropagation();
218 | //window.event.returnValue=false;
219 | }).bind(this);
220 | var onDrop = (function (e) {
221 | e.preventDefault();
222 | e.stopPropagation();
223 | var dropPlace = { node: node, parentNode: this.props.node };
224 | this.props.executeAction("onDrop", dropPlace);
225 | }).bind(this);
226 |
227 | var type = node.elementName;
228 |
229 | var containers = node.containers || [];
230 | var boxes = node.boxes || [];
231 |
232 | var selected = this.props.current.node === node;
233 | var parentSelected = this.props.current.parentNode === node;
234 | var path = this.props.path + '.containers[' + i + ']';
235 |
236 | var classes = (0, _classnames2['default'])({
237 | 'node': true,
238 | 'selected': selected,
239 | 'parentSelected': this.props.parentSelected
240 | });
241 |
242 | var label = _react2['default'].createElement(
243 | 'span',
244 | { draggable: 'true', onDragEnter: onDragEnter,
245 | onDragStart: onDragStart,
246 | onDragOver: onDragOver, onDrop: onDrop, className: classes, onClick: this.handleClick.bind(this, node, path) },
247 | node.name
248 | );
249 | return _react2['default'].createElement(
250 | _reactTreeview2['default'],
251 | { key: type + '|' + i, nodeLabel: label, defaultCollapsed: false },
252 | _react2['default'].createElement(TreeNode, { key: node.name + '|' + i, node: node, path: path, current: this.props.current, currentChanged: this.props.currentChanged.bind(this), filterText: this.props.filterText, executeAction: this.props.executeAction.bind(this) }),
253 | boxes.map(function (box, j) {
254 |
255 | var onDragStart1 = (function (e) {
256 | console.log('drag started');
257 | e.stopPropagation();
258 | var draggingItem = {
259 | node: box,
260 | parentNode: node };
261 | this.props.executeAction("onDragStart", draggingItem);
262 | }).bind(this);
263 |
264 | if (box.name.toLowerCase().indexOf(this.props.filterText.toLowerCase()) === -1) {
265 | return;
266 | }
267 |
268 | var boxPath = path + '.boxes[' + j + ']';
269 | var classes = (0, _classnames2['default'])({
270 | 'node': true,
271 | 'selected': this.props.current.node === box
272 | });
273 | return _react2['default'].createElement(
274 | 'div',
275 | { draggable: 'true', className: classes, onDragStart: onDragStart1, onClick: this.handleClick.bind(this, box, boxPath), key: box.name + j },
276 | _react2['default'].createElement(
277 | 'span',
278 | null,
279 | box.name
280 | )
281 | );
282 | }, this)
283 | );
284 | }, this)
285 | );
286 | }
287 | }]);
288 |
289 | return TreeNode;
290 | })(_react2['default'].Component);
291 |
292 | module.exports = exports['default'];
--------------------------------------------------------------------------------
/lib/components/Workplace.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, '__esModule', {
4 | value: true
5 | });
6 |
7 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
8 |
9 | var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
10 |
11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
12 |
13 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
14 |
15 | function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
16 |
17 | var _propTypes = require('prop-types');
18 |
19 | var _propTypes2 = _interopRequireDefault(_propTypes);
20 |
21 | var _lodash = require('lodash');
22 |
23 | var _lodash2 = _interopRequireDefault(_lodash);
24 |
25 | var _reactDndHtml5Backend = require('react-dnd-html5-backend');
26 |
27 | var _reactDndHtml5Backend2 = _interopRequireDefault(_reactDndHtml5Backend);
28 |
29 | var _reactDnd = require('react-dnd');
30 |
31 | var _transhand = require('transhand');
32 |
33 | var _workplaceContainer = require('./../workplace/Container');
34 |
35 | var _workplaceContainer2 = _interopRequireDefault(_workplaceContainer);
36 |
37 | var _utilBackgroundStyle = require('../util/backgroundStyle');
38 |
39 | var _utilBackgroundStyle2 = _interopRequireDefault(_utilBackgroundStyle);
40 |
41 | var React = require('react');
42 |
43 | var DEFAULT_TRANSFORM = {
44 | tx: 0, ty: 0, //translate in px
45 | sx: 1, sy: 1, //scale
46 | rz: 0, //rotation in radian
47 | ox: 0.5, oy: 0.5 //transform origin
48 | };
49 |
50 | var DEFAULT_ROOT_PATH = 'path';
51 |
52 | var Workplace = (function (_React$Component) {
53 | _inherits(Workplace, _React$Component);
54 |
55 | function Workplace(props) {
56 | _classCallCheck(this, Workplace);
57 |
58 | _get(Object.getPrototypeOf(Workplace.prototype), 'constructor', this).call(this, props);
59 | this.state = {};
60 | }
61 |
62 | _createClass(Workplace, [{
63 | key: 'getChildContext',
64 | value: function getChildContext() {
65 | return { snapGrid: this.props.snapGrid };
66 | }
67 | }, {
68 | key: 'handleChange',
69 | value: function handleChange(change) {
70 | var currentNode = this.props.current.node;
71 | if (currentNode == undefined) return;
72 |
73 | var style = currentNode.style;
74 | if (style === undefined) return;
75 |
76 | //resolution strategy -> defaultTransform -> style.transform -> change
77 | var transform = _lodash2['default'].merge(_lodash2['default'].merge(_lodash2['default'].clone(DEFAULT_TRANSFORM), style.transform), change);
78 |
79 | var updated = currentNode.set('style', _lodash2['default'].extend(_lodash2['default'].clone(style), { 'transform': transform }));
80 | this.props.currentChanged(updated);
81 | }
82 | }, {
83 | key: 'currentChanged',
84 | value: function currentChanged(node, path, domEl) {
85 | if (this.props.currentChanged !== undefined) this.props.currentChanged(node, path);
86 | this.setState({
87 | currentDOMNode: domEl
88 | });
89 | }
90 | }, {
91 | key: 'render',
92 | value: function render() {
93 | var _props = this.props;
94 | var schema = _props.schema;
95 | var current = _props.current;
96 | var currentChanged = _props.currentChanged;
97 | var dataContext = _props.dataContext;
98 |
99 | var handleClick = function handleClick() {
100 | if (currentChanged !== undefined) currentChanged(schema, DEFAULT_ROOT_PATH);
101 | };
102 |
103 | var style = current.node && current.node.style || {};
104 | var transform = _lodash2['default'].merge(_lodash2['default'].clone(DEFAULT_TRANSFORM), style.transform);
105 |
106 | var ctx = schema.props && schema.props.context || {};
107 | var customStyles = ctx['styles'] || {};
108 | var code = ctx['code'] && ctx['code'].compiled;
109 | var customCode = !!code ? eval(code) : undefined;
110 |
111 | //append shared code to data context
112 | if (dataContext !== undefined) dataContext.customCode = customCode;
113 |
114 | var context = {
115 | styles: customStyles,
116 | customCode: customCode
117 | };
118 |
119 | var bg = schema.props && schema.props.background || {};
120 | var bgStyle = (0, _utilBackgroundStyle2['default'])(bg);
121 |
122 | bgStyle.position = 'absolute';
123 | bgStyle.width = '100%';
124 | bgStyle.height = '100%';
125 | bgStyle.zIndex = -1;
126 |
127 | var component = React.createElement(_workplaceContainer2['default'], {
128 | containers: schema.containers,
129 | boxes: schema.boxes,
130 | currentChanged: this.currentChanged.bind(this),
131 | current: current,
132 | path: DEFAULT_ROOT_PATH,
133 | handleClick: handleClick,
134 | isRoot: true,
135 | node: schema,
136 | dataBinder: dataContext,
137 | ctx: context,
138 | widgets: this.props.widgets,
139 | widgetRenderer: this.props.widgetRenderer
140 | });
141 |
142 | return React.createElement(
143 | 'div',
144 | { className: 'cWorkplace' },
145 | React.createElement('div', { style: bgStyle }),
146 | component,
147 | this.state.currentDOMNode !== undefined ? React.createElement(_transhand.CSSTranshand, { transform: transform, deTarget: this.state.currentDOMNode,
148 | onChange: this.handleChange.bind(this) }) : null
149 | );
150 | }
151 | }]);
152 |
153 | return Workplace;
154 | })(React.Component);
155 |
156 | ;
157 | Workplace.childContextTypes = { snapGrid: _propTypes2['default'].arrayOf(_propTypes2['default'].number) };
158 | exports['default'] = (0, _reactDnd.DragDropContext)(_reactDndHtml5Backend2['default'])(Workplace);
159 | module.exports = exports['default'];
--------------------------------------------------------------------------------
/lib/util/ItemTypes.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, '__esModule', {
4 | value: true
5 | });
6 | exports['default'] = {
7 | BLOCK: 'block',
8 | IMAGE: 'image',
9 | BOX: 'box',
10 | CONTAINER: 'container',
11 | RESIZABLE_HANDLE: 'resize',
12 | TREE_NODE: 'treeNode'
13 | };
14 | module.exports = exports['default'];
--------------------------------------------------------------------------------
/lib/util/backgroundStyle.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
8 |
9 | var _lodash = require('lodash');
10 |
11 | var _lodash2 = _interopRequireDefault(_lodash);
12 |
13 | exports["default"] = function (source, panelSize) {
14 | var bg = source;
15 | var bgStyle = {};
16 | if (bg === undefined) return bgStyle;
17 |
18 | //size
19 | if (!!bg.size) {
20 | if (panelSize !== undefined && (bg.size === "leftHalf" || bg.size === "rightHalf")) {
21 | bgStyle.backgroundSize = panelSize.width * 2 + "px " + panelSize.height + "px";
22 | bgStyle.backgroundPosition = bg.size === "leftHalf" ? '0% 0%' : '100% 0%';
23 | //console.log(bgStyle);
24 | } else {
25 | bgStyle.backgroundSize = bg.size;
26 | if (!!bg.position) bgStyle.backgroundPosition = bg.position;
27 | }
28 | }
29 | //gradient
30 | var bgGradient = bg.gradient;
31 | if (bgGradient !== undefined) {
32 | //gradient
33 | if (bgGradient.stops !== undefined) {
34 | var gradientStops = _lodash2["default"].reduce(bgGradient.stops, function (memo, stop) {
35 | return memo + ', ' + stop.color + ' ' + stop.offset * 100 + '%';
36 | }, '');
37 |
38 | var orientation = bgGradient.orientation || 'top';
39 | var grandientType = bgGradient.orientation === 'center, ellipse cover' ? '-webkit-radial-gradient' : '-webkit-linear-gradient';
40 | bgStyle.background = grandientType + '(' + orientation + gradientStops + ')';
41 | }
42 | }
43 |
44 | //color
45 | var bgColor = bg.color;
46 | if (bgColor !== undefined) {
47 | if (!!bgColor.color) bgStyle.backgroundColor = bgColor.color;
48 | if (!!bgColor.alpha) bgStyle.opacity = bgColor.alpha / 100;
49 | }
50 |
51 | if (!!bg.image) bgStyle.backgroundImage = 'url(' + bg.image + ')';
52 | if (!!bg.repeat) bgStyle.backgroundRepeat = bg.repeat;
53 | if (!!bg.attachment) bgStyle.backgroundAttachment = bg.attachment;
54 |
55 | var filter = bg.filter || {};
56 | var cssFilter = "";
57 | if (!!filter.blur) cssFilter += ' blur(' + filter.blur + 'px)';
58 | if (!!filter.brightness) cssFilter += ' brightness(' + filter.brightness + '%)';
59 | if (!!filter.contrast) cssFilter += ' contrast(' + filter.contrast + '%)';
60 | if (!!filter.grayscale) cssFilter += ' grayscale(' + filter.grayscale + '%)';
61 | if (!!filter.hueRotate) cssFilter += ' hue-rotate(' + filter.hueRotate + 'deg)';
62 | if (!!filter.invert) cssFilter += ' invert(' + filter.invert + '%)';
63 | if (!!filter.opacity) cssFilter += ' opacity(' + filter.opacity + '%)';
64 | if (!!filter.saturate) cssFilter += ' saturate(' + filter.saturate + '%)';
65 | if (!!filter.sepia) cssFilter += ' sepia(' + filter.sepia + '%)';
66 |
67 | if (!!cssFilter) {
68 | bgStyle.WebkitFilter = cssFilter;
69 | bgStyle.filter = cssFilter;
70 | }
71 | //bgStyle.position = 'absolute';
72 | return bgStyle;
73 | };
74 |
75 | module.exports = exports["default"];
--------------------------------------------------------------------------------
/lib/util/generateCssTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, '__esModule', {
4 | value: true
5 | });
6 | exports['default'] = generateCssTransform;
7 |
8 | function generateCssTransform(transform) {
9 | var cssTransform = '';
10 |
11 | if (transform.tx !== undefined) cssTransform += ' translateX(' + transform.tx + 'px)';
12 | if (transform.ty !== undefined) cssTransform += ' translateY(' + transform.ty + 'px)';
13 | if (transform.rz !== undefined) cssTransform += ' rotate(' + transform.rz + 'rad)';
14 | if (transform.sx !== undefined) cssTransform += ' scaleX(' + transform.sx + ')';
15 | if (transform.sy !== undefined) cssTransform += ' scaleY(' + transform.sy + ')';
16 |
17 | return cssTransform;
18 | }
19 |
20 | module.exports = exports['default'];
--------------------------------------------------------------------------------
/lib/workplace/Box.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, '__esModule', {
4 | value: true
5 | });
6 |
7 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
8 |
9 | var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
10 |
11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
12 |
13 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
14 |
15 | function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
16 |
17 | var _react = require('react');
18 |
19 | var _react2 = _interopRequireDefault(_react);
20 |
21 | var _propTypes = require('prop-types');
22 |
23 | var _propTypes2 = _interopRequireDefault(_propTypes);
24 |
25 | var _reactDom = require('react-dom');
26 |
27 | var _reactDom2 = _interopRequireDefault(_reactDom);
28 |
29 | var _reactDnd = require('react-dnd');
30 |
31 | var _lodash = require('lodash');
32 |
33 | var _lodash2 = _interopRequireDefault(_lodash);
34 |
35 | var _classnames = require('classnames');
36 |
37 | var _classnames2 = _interopRequireDefault(_classnames);
38 |
39 | var _ResizeContainerJs = require('./ResizeContainer.js');
40 |
41 | var _ResizeContainerJs2 = _interopRequireDefault(_ResizeContainerJs);
42 |
43 | var _utilItemTypesJs = require('../util/ItemTypes.js');
44 |
45 | var _utilItemTypesJs2 = _interopRequireDefault(_utilItemTypesJs);
46 |
47 | var _utilGenerateCssTransform = require('../util/generateCssTransform');
48 |
49 | var _utilGenerateCssTransform2 = _interopRequireDefault(_utilGenerateCssTransform);
50 |
51 | /**
52 | * Implements the drag source contract.
53 | */
54 | var source = {
55 | beginDrag: function beginDrag(props, monitor, component) {
56 | return {
57 | //effectAllowed: DropEffects.MOVE,
58 | item: component.props
59 | };
60 | }
61 | };
62 |
63 | /**
64 | * Specifies the props to inject into your component.
65 | */
66 | function collect(connect, monitor) {
67 | return {
68 | connectDragSource: connect.dragSource(),
69 | isDragging: monitor.isDragging()
70 | };
71 | }
72 |
73 | var propTypes = {
74 | // Injected by React DnD:
75 | isDragging: _propTypes2['default'].bool.isRequired,
76 | connectDragSource: _propTypes2['default'].func.isRequired
77 | };
78 |
79 | var Box = (function (_React$Component) {
80 | _inherits(Box, _React$Component);
81 |
82 | function Box() {
83 | _classCallCheck(this, Box);
84 |
85 | _get(Object.getPrototypeOf(Box.prototype), 'constructor', this).apply(this, arguments);
86 | }
87 |
88 | _createClass(Box, [{
89 | key: 'handleDoubleClick',
90 | value: function handleDoubleClick(e) {
91 | e.stopPropagation();
92 | if (this.props.currentChanged !== undefined) this.props.currentChanged(this.props.node, this.props.path, _reactDom2['default'].findDOMNode(this));
93 | }
94 | }, {
95 | key: 'handleClick',
96 | value: function handleClick(e) {
97 | e.stopPropagation();
98 | if (this.props.currentChanged !== undefined) this.props.currentChanged(this.props.node, this.props.path);
99 | }
100 | }, {
101 | key: 'shouldComponentUpdate',
102 | value: function shouldComponentUpdate(nextProps) {
103 |
104 | // The comparison is fast, and we won't render the component if
105 | // it does not need it. This is a huge gain in performance.
106 | var box = this.props.node;
107 | var update = box !== nextProps.node || this.props.selected != nextProps.selected;
108 | //console.log(nextProps.node.name + ' : ' + update);
109 | if (update) return update;
110 |
111 | //test -> widget custom style changed
112 | var propsStyles = this.props.ctx.styles;
113 | var nextPropsStyles = nextProps.ctx.styles;
114 | update = (propsStyles && propsStyles[box.elementName]) !== (nextPropsStyles && nextPropsStyles[box.elementName]);
115 |
116 | return update;
117 | }
118 |
119 | // componentWillReceiveProps(nextProps){
120 | // var index = this.props.index;
121 | // for (var action of ['right','left','up','down'])
122 | // {
123 | // if (nextProps.selected)this.props.bindShortcut(action, this.props.moveBox.bind(this,index,action));//: this.props.unbindShortcut(action);
124 | // }
125 | // }
126 | }, {
127 | key: 'render',
128 | value: function render() {
129 | var _props = this.props;
130 | var widgets = _props.widgets;
131 | var widgetRenderer = _props.widgetRenderer;
132 | var selected = _props.selected;
133 | var node = _props.node;
134 | var dataBinder = _props.dataBinder;
135 | var currentChanged = _props.currentChanged;
136 | var index = _props.index;
137 | var _props2 = this.props;
138 | var isDragging = _props2.isDragging;
139 | var connectDragSource = _props2.connectDragSource;
140 | var item = _props2.item;
141 |
142 | //prepare styles
143 | var classes = (0, _classnames2['default'])({
144 | 'box': true,
145 | 'selected': selected
146 | });
147 |
148 | //clone node
149 | var box = node.toJS();
150 |
151 | //document custom style
152 | var ctx = this.props.ctx || {};
153 | var customStyle = ctx["styles"] && ctx["styles"][box.elementName];
154 |
155 | //specific props resolution rule -> propagate width and height from style to widget props
156 | var boxProps = box.props || {};
157 | var boxStyle = box.style || {};
158 | if (!boxProps.width && !!boxStyle.width) boxProps.width = boxStyle.width;
159 | if (!boxProps.height && !!boxStyle.height) boxProps.height = boxStyle.height;
160 |
161 | var boxComponent = widgetRenderer !== undefined && widgets !== undefined ? _react2['default'].createElement(widgetRenderer, {
162 | tabIndex: index,
163 | widget: widgets[box.elementName],
164 | node: box,
165 | dataBinder: dataBinder,
166 | customStyle: customStyle,
167 | customCode: ctx['customCode'],
168 | designer: true,
169 | current: node,
170 | currentChanged: currentChanged,
171 | selected: selected
172 | }, null) : _react2['default'].createElement(
173 | 'div',
174 | null,
175 | 'No widget renderer or widget factory provided.'
176 | );
177 |
178 | //create style
179 | var styles = _lodash2['default'].extend({ position: this.props.position }, boxStyle);
180 | if (boxStyle.transform !== undefined) styles['transform'] = (0, _utilGenerateCssTransform2['default'])(boxStyle.transform);
181 |
182 | //wrap with div double click for transhand transformation
183 | if (box.elementName !== "Core.RichTextContent") boxComponent = _react2['default'].createElement(
184 | 'div',
185 | { onDoubleClick: this.handleDoubleClick.bind(this) },
186 | boxComponent
187 | );
188 |
189 | return connectDragSource(_react2['default'].createElement(
190 | 'div',
191 | { style: styles, className: classes, onClick: this.handleClick.bind(this) },
192 | _react2['default'].createElement(
193 | _ResizeContainerJs2['default'],
194 | { node: node, currentChanged: currentChanged },
195 | boxComponent
196 | )
197 | ));
198 | }
199 | }]);
200 |
201 | return Box;
202 | })(_react2['default'].Component);
203 |
204 | ;
205 |
206 | Box.propTypes = propTypes;
207 | // Export the wrapped component:
208 | exports['default'] = (0, _reactDnd.DragSource)(_utilItemTypesJs2['default'].BOX, source, collect)(Box);
209 | module.exports = exports['default'];
--------------------------------------------------------------------------------
/lib/workplace/Container.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, '__esModule', {
4 | value: true
5 | });
6 |
7 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
8 |
9 | var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
10 |
11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
12 |
13 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
14 |
15 | function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
16 |
17 | var _react = require('react');
18 |
19 | var _react2 = _interopRequireDefault(_react);
20 |
21 | var _propTypes = require('prop-types');
22 |
23 | var _propTypes2 = _interopRequireDefault(_propTypes);
24 |
25 | var _utilItemTypesJs = require('../util/ItemTypes.js');
26 |
27 | var _utilItemTypesJs2 = _interopRequireDefault(_utilItemTypesJs);
28 |
29 | var _reactDnd = require('react-dnd');
30 |
31 | var _lodash = require('lodash');
32 |
33 | var _lodash2 = _interopRequireDefault(_lodash);
34 |
35 | var _classnames = require('classnames');
36 |
37 | var _classnames2 = _interopRequireDefault(_classnames);
38 |
39 | var _Box = require('./Box');
40 |
41 | var _Box2 = _interopRequireDefault(_Box);
42 |
43 | var _ResizableHandleJs = require('./ResizableHandle.js');
44 |
45 | var _ResizableHandleJs2 = _interopRequireDefault(_ResizableHandleJs);
46 |
47 | var HANDLE_OFFSET = 8;
48 |
49 | var snapToGrid = function snapToGrid(grid, deltaX, deltaY) {
50 | var x = Math.round(deltaX / grid[0]) * grid[0];
51 | var y = Math.round(deltaY / grid[1]) * grid[1];
52 | return [x, y];
53 | };
54 |
55 | var target = {
56 | drop: function drop(props, monitor, component) {
57 | if (monitor.didDrop()) {
58 | // If you want, you can check whether some nested
59 | // target already handled drop
60 | return;
61 | }
62 |
63 | var item = monitor.getItem().item;
64 |
65 | var delta = monitor.getDifferenceFromInitialOffset();
66 |
67 | if (!!!delta) return;
68 |
69 | if (monitor.getItemType() === _utilItemTypesJs2['default'].BOX) {
70 | var left = Math.round(isNaN(item.left) ? 0 : parseInt(item.left, 10) + delta.x);
71 | var top = Math.round(isNaN(item.top) ? 0 : parseInt(item.top, 10) + delta.y);
72 |
73 | component.moveBox(item.index, left, top);
74 | }
75 |
76 | if (monitor.getItemType() === _utilItemTypesJs2['default'].RESIZABLE_HANDLE) {
77 | var left = Math.round(delta.x < 0 ? delta.x + HANDLE_OFFSET : delta.x - HANDLE_OFFSET);
78 | var top = Math.round(delta.y < 0 ? delta.y + HANDLE_OFFSET : delta.y - HANDLE_OFFSET);
79 | component.resizeContainer(item.parent, left, top);
80 | }
81 | }
82 | };
83 |
84 | var Container = (function (_React$Component) {
85 | _inherits(Container, _React$Component);
86 |
87 | function Container() {
88 | _classCallCheck(this, Container);
89 |
90 | _get(Object.getPrototypeOf(Container.prototype), 'constructor', this).apply(this, arguments);
91 | }
92 |
93 | _createClass(Container, [{
94 | key: 'shouldComponentUpdate',
95 | value: function shouldComponentUpdate(nextProps, nextState) {
96 |
97 | // The comparison is fast, and we won't render the component if
98 | // it does not need it. This is a huge gain in performance.
99 | var node = this.props.node;
100 | var current = this.props.current;
101 | var update = node !== nextProps.node || (current && current.path) != (nextProps.current && nextProps.current.path);
102 |
103 | if (update) return update;
104 |
105 | //test -> container custom style changed
106 | var propsStyles = this.props.ctx.styles;
107 | var nextPropsStyles = nextProps.ctx.styles;
108 | update = propsStyles !== nextPropsStyles;
109 |
110 | return update;
111 | }
112 | }, {
113 | key: 'moveBox',
114 | value: function moveBox(index, left, top) {
115 | var deltas = snapToGrid(this.context.snapGrid, left, top);
116 | this.moveBoxEx(index, deltas[0], deltas[1]);
117 | }
118 | }, {
119 | key: 'moveBoxEx',
120 | value: function moveBoxEx(index, left, top) {
121 | var boxes = this.props.boxes;
122 | if (boxes === undefined) return;
123 | var box = boxes[index];
124 | if (box === undefined) return;
125 |
126 | var updated = box.set({ 'style': _lodash2['default'].merge(_lodash2['default'].clone(box.style), { 'left': left, 'top': top }) });
127 | this.props.currentChanged(updated);
128 | }
129 |
130 | // moveBoxToDirection(index, direction) {
131 | // var boxes = this.props.boxes;
132 | // if (boxes === undefined) return;
133 | // var box = boxes[index];
134 | // if (box === undefined) return;
135 |
136 | // var deltas = this.getDirectionDeltas(direction);
137 | // var updated = box.set({ 'style': _.merge(_.clone(box.style), { 'left': (box.style.left || 0) + deltas[0], 'top': (box.style.top || 0) + deltas[1], }) });
138 | // this.props.currentChanged(updated);
139 | // }
140 | }, {
141 | key: 'getDirectionDeltas',
142 | value: function getDirectionDeltas(direction) {
143 | var snaps = this.context.snapGrid;
144 | var deltas = [0, 0];
145 | switch (direction) {
146 | case "left":
147 | return [-1 * snaps[0], 0];
148 | case "right":
149 | return [snaps[0], 0];
150 | case "up":
151 | return [0, -1 * snaps[1]];
152 | case "down":
153 | return [0, snaps[1]];
154 | default:
155 | return deltas;
156 | }
157 | }
158 | }, {
159 | key: 'resizeContainer',
160 | value: function resizeContainer(container, deltaWidth, deltaHeight) {
161 | if (container === undefined) return;
162 |
163 | //TODO: use merge instead of clone
164 | var style = _lodash2['default'].clone(container.style) || {};
165 | var newWidth = (style.width || 0) + deltaWidth;
166 | if (newWidth < 0) return;
167 | var newHeight = (style.height || 0) + deltaHeight;
168 | if (newHeight < 0) return;
169 |
170 | var deltas = snapToGrid(this.context.snapGrid, newWidth, newHeight);
171 | style.width = deltas[0];
172 | style.height = deltas[1];
173 |
174 | var updated = container.set({ 'style': style });
175 | this.props.currentChanged(updated);
176 | }
177 | }, {
178 | key: 'handleClick',
179 | value: function handleClick(e) {
180 | e.stopPropagation();
181 | if (this.props.handleClick !== undefined) this.props.handleClick();
182 | }
183 | }, {
184 | key: 'render',
185 | value: function render() {
186 | var _props = this.props;
187 | var elementName = _props.elementName;
188 | var ctx = _props.ctx;
189 | var widgets = _props.widgets;
190 | var widgetRenderer = _props.widgetRenderer;
191 | var current = _props.current;
192 | var currentChanged = _props.currentChanged;
193 | var node = _props.node;
194 | var parent = _props.parent;
195 | var dataBinder = _props.dataBinder;
196 | var _props2 = this.props;
197 | var canDrop = _props2.canDrop;
198 | var isOver = _props2.isOver;
199 | var connectDropTarget = _props2.connectDropTarget;
200 |
201 | var containers = this.props.containers || [];
202 | var boxes = this.props.boxes || [];
203 |
204 | //styles
205 | var classes = (0, _classnames2['default'])({
206 | 'con': true,
207 | 'selected': this.props.selected,
208 | 'parentSelected': this.props.parentSelected,
209 | 'root': this.props.isRoot
210 | });
211 |
212 | var styles = {
213 | left: this.props.left,
214 | top: this.props.top,
215 | height: this.props.height,
216 | width: this.props.width,
217 | position: this.props.position || 'relative'
218 | };
219 |
220 | var nodeProps = node.props;
221 | var nodeBindings = node.bindings || {};
222 |
223 | //apply custom styles
224 | var customStyle = ctx["styles"] && ctx["styles"][elementName];
225 | if (customStyle !== undefined) nodeProps = _lodash2['default'].merge(_lodash2['default'].cloneDeep(customStyle), nodeProps);
226 |
227 | //apply node props
228 | if (dataBinder !== undefined && widgetRenderer) nodeProps = widgetRenderer.bindProps(_lodash2['default'].cloneDeep(nodeProps), nodeBindings.bindings, dataBinder, true);
229 |
230 | var containerComponent = widgets[elementName] || 'div';
231 |
232 | return connectDropTarget(_react2['default'].createElement(
233 | 'div',
234 | { className: classes, style: styles, onClick: this.handleClick.bind(this) },
235 | _react2['default'].createElement(
236 | 'div',
237 | null,
238 | containers.length !== 0 ? _react2['default'].createElement(containerComponent, nodeProps, containers.map(function (container, index) {
239 |
240 | var selected = container === current.node;
241 | var parentSelected = false; //container === current.parentNode;
242 | var key = container.name + index;
243 | var containerStyle = container.style || {};
244 |
245 | var path = this.props.path + '.containers[' + index + ']';
246 |
247 | var handleClick = function handleClick() {
248 | if (currentChanged !== undefined) currentChanged(container, path);
249 | };
250 |
251 | var left = containerStyle.left === undefined ? 0 : parseInt(containerStyle.left, 10);
252 | var top = containerStyle.top === undefined ? 0 : parseInt(containerStyle.top, 10);
253 |
254 | var childProps = _lodash2['default'].cloneDeep(container.props) || {};
255 | var childBindings = container.bindings || {};
256 |
257 | //apply custom styles
258 | var childCustomStyle = ctx["styles"] && ctx["styles"][container.elementName];
259 | if (childCustomStyle !== undefined) childProps = _lodash2['default'].merge(_lodash2['default'].cloneDeep(childCustomStyle), childProps);
260 |
261 | //apply node props
262 | if (dataBinder !== undefined && widgetRenderer) childProps = widgetRenderer.bindProps(childProps, childBindings.bindings, dataBinder, true);
263 |
264 | //specific props resolution rule -> propagate width and height from style to child container props
265 |
266 | if (!childProps.width && !!containerStyle.width) childProps.width = containerStyle.width;
267 | if (!childProps.height && !!containerStyle.height) childProps.height = containerStyle.height;
268 | if (!childProps.left && !!containerStyle.left) childProps.left = containerStyle.left;
269 | if (!childProps.top && !!containerStyle.top) childProps.top = containerStyle.top;
270 |
271 | var applyDirectChildContainers = elementName == "Grid"; //container.containers && container.containers.length === 0;
272 | //var childComponent = 'div';
273 | var wrappedContainer = _react2['default'].createElement(WrappedContainer, { elementName: container.elementName,
274 | index: index,
275 | left: left,
276 | top: top,
277 | height: containerStyle.height,
278 | width: containerStyle.width,
279 | position: containerStyle.position || 'relative',
280 | boxes: container.boxes,
281 | containers: container.containers,
282 | node: container,
283 | path: path,
284 | parent: parent,
285 | currentChanged: currentChanged,
286 | current: current,
287 | handleClick: handleClick,
288 | parentSelected: parentSelected,
289 | selected: selected,
290 | dataBinder: dataBinder,
291 | ctx: ctx,
292 | widgets: widgets,
293 | widgetRenderer: widgetRenderer
294 | });
295 |
296 | return applyDirectChildContainers ? _react2['default'].createElement(widgets[container.elementName] || 'div', _lodash2['default'].extend(childProps, { child: true, key: key }), wrappedContainer) : wrappedContainer;
297 | }, this)) : null,
298 | boxes.map(function (box, index) {
299 |
300 | var selected = box === current.node;
301 | var key = box.name + index;
302 |
303 | var boxStyle = box.style || {};
304 | var left = boxStyle.left === undefined ? 0 : parseInt(box.style.left, 10);
305 | var top = boxStyle.top === undefined ? 0 : parseInt(box.style.top, 10);
306 |
307 | var path = this.props.path + '.boxes[' + index + ']';
308 |
309 | var box = _react2['default'].createElement(_Box2['default'], { key: key,
310 | index: index,
311 | left: left,
312 | top: top,
313 | path: path,
314 | position: elementName === "Cell" ? 'relative' : 'absolute',
315 | selected: selected,
316 | hideSourceOnDrag: this.props.hideSourceOnDrag,
317 | currentChanged: currentChanged,
318 | node: box, dataBinder: dataBinder,
319 | ctx: ctx,
320 | widgets: widgets,
321 | widgetRenderer: widgetRenderer
322 | });
323 |
324 | return box;
325 | }, this)
326 | ),
327 | this.props.isRoot || this.props.width === undefined || this.props.height === undefined ? null : _react2['default'].createElement(_ResizableHandleJs2['default'], { parent: this.props.node })
328 | ));
329 | }
330 | }]);
331 |
332 | return Container;
333 | })(_react2['default'].Component);
334 |
335 | Container.contextTypes = {
336 | snapGrid: _propTypes2['default'].arrayOf(_propTypes2['default'].number)
337 | };
338 |
339 | var collect = function collect(connect, monitor) {
340 | return {
341 | connectDropTarget: connect.dropTarget(),
342 | isOver: monitor.isOver(),
343 | canDrop: monitor.canDrop()
344 | };
345 | };
346 | var WrappedContainer = (0, _reactDnd.DropTarget)([_utilItemTypesJs2['default'].RESIZABLE_HANDLE, _utilItemTypesJs2['default'].BOX], target, collect)(Container);
347 | exports['default'] = WrappedContainer;
348 | module.exports = exports['default'];
--------------------------------------------------------------------------------
/lib/workplace/ResizableHandle.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, '__esModule', {
4 | value: true
5 | });
6 |
7 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
8 |
9 | var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
10 |
11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
12 |
13 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
14 |
15 | function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
16 |
17 | var _react = require('react');
18 |
19 | var _react2 = _interopRequireDefault(_react);
20 |
21 | var _propTypes = require('prop-types');
22 |
23 | var _propTypes2 = _interopRequireDefault(_propTypes);
24 |
25 | var _reactDnd = require('react-dnd');
26 |
27 | var _utilItemTypesJs = require('../util/ItemTypes.js');
28 |
29 | var _utilItemTypesJs2 = _interopRequireDefault(_utilItemTypesJs);
30 |
31 | /**
32 | * Implements the drag source contract.
33 | */
34 | var source = {
35 | beginDrag: function beginDrag(props, monitor, component) {
36 | return {
37 | //effectAllowed: DropEffects.MOVE,
38 | item: component.props
39 | };
40 | }
41 | };
42 |
43 | /**
44 | * Specifies the props to inject into your component.
45 | */
46 | function collect(connect, monitor) {
47 | return {
48 |
49 | connectDragSource: connect.dragSource(),
50 | isDragging: monitor.isDragging()
51 | };
52 | }
53 | var propTypes = {
54 | //item: PropTypes.isRequired,
55 |
56 | // Injected by React DnD:
57 | isDragging: _propTypes2['default'].bool.isRequired,
58 | connectDragSource: _propTypes2['default'].func.isRequired
59 | };
60 |
61 | var ResizableHandle = (function (_React$Component) {
62 | _inherits(ResizableHandle, _React$Component);
63 |
64 | function ResizableHandle() {
65 | _classCallCheck(this, ResizableHandle);
66 |
67 | _get(Object.getPrototypeOf(ResizableHandle.prototype), 'constructor', this).apply(this, arguments);
68 | }
69 |
70 | _createClass(ResizableHandle, [{
71 | key: 'render',
72 | value: function render() {
73 | var _props = this.props;
74 | var isDragging = _props.isDragging;
75 | var connectDragSource = _props.connectDragSource;
76 | var item = _props.item;
77 |
78 | return connectDragSource(_react2['default'].createElement('div', { className: 'resizable-handle' }));
79 | }
80 | }]);
81 |
82 | return ResizableHandle;
83 | })(_react2['default'].Component);
84 |
85 | ;
86 |
87 | ResizableHandle.propTypes = propTypes;
88 |
89 | // Export the wrapped component:
90 | exports['default'] = (0, _reactDnd.DragSource)(_utilItemTypesJs2['default'].RESIZABLE_HANDLE, source, collect)(ResizableHandle);
91 | module.exports = exports['default'];
--------------------------------------------------------------------------------
/lib/workplace/ResizeContainer.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, '__esModule', {
4 | value: true
5 | });
6 |
7 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
8 |
9 | var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
10 |
11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
12 |
13 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
14 |
15 | function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
16 |
17 | var _react = require('react');
18 |
19 | var _react2 = _interopRequireDefault(_react);
20 |
21 | var _propTypes = require('prop-types');
22 |
23 | var _propTypes2 = _interopRequireDefault(_propTypes);
24 |
25 | var _utilItemTypesJs = require('../util/ItemTypes.js');
26 |
27 | var _utilItemTypesJs2 = _interopRequireDefault(_utilItemTypesJs);
28 |
29 | var _reactDnd = require('react-dnd');
30 |
31 | var _lodash = require('lodash');
32 |
33 | var _lodash2 = _interopRequireDefault(_lodash);
34 |
35 | var _ResizableHandleJs = require('./ResizableHandle.js');
36 |
37 | var _ResizableHandleJs2 = _interopRequireDefault(_ResizableHandleJs);
38 |
39 | var target = {
40 | drop: function drop(props, monitor, component) {
41 | if (monitor.didDrop()) {
42 | // If you want, you can check whether some nested
43 | // target already handled drop
44 | return;
45 | }
46 |
47 | var item = monitor.getItem().item;
48 |
49 | var delta = monitor.getDifferenceFromInitialOffset();
50 |
51 | if (!!!delta) return;
52 |
53 | if (monitor.getItemType() === _utilItemTypesJs2['default'].RESIZABLE_HANDLE) {
54 | var left = Math.round(delta.x);
55 | var top = Math.round(delta.y);
56 |
57 | component.resizeContainer(item.parent, left, top);
58 | };
59 | }
60 | };
61 | var HANDLE_OFFSET = 30;
62 |
63 | var ResizeContainer = (function (_React$Component) {
64 | _inherits(ResizeContainer, _React$Component);
65 |
66 | function ResizeContainer() {
67 | _classCallCheck(this, ResizeContainer);
68 |
69 | _get(Object.getPrototypeOf(ResizeContainer.prototype), 'constructor', this).apply(this, arguments);
70 | }
71 |
72 | _createClass(ResizeContainer, [{
73 | key: 'resizeContainer',
74 | value: function resizeContainer(container, deltaWidth, deltaHeight) {
75 | if (container === undefined) return;
76 |
77 | //TODO: use merge instead of clone
78 | var style = _lodash2['default'].cloneDeep(container.style);
79 | style.width += deltaWidth;
80 | style.height += deltaHeight;
81 |
82 | var updated = container.set({ 'style': style });
83 | this.props.currentChanged(updated);
84 | }
85 | }, {
86 | key: 'render',
87 | value: function render() {
88 | var _props = this.props;
89 | var canDrop = _props.canDrop;
90 | var isOver = _props.isOver;
91 | var connectDropTarget = _props.connectDropTarget;
92 |
93 | var style = this.props.node && this.props.node.style || {};
94 |
95 | //resize handle position
96 | var useResize = !!style.height && !!style.width;
97 | var resizeHandlePosition = { top: style.height - HANDLE_OFFSET, left: style.width - HANDLE_OFFSET };
98 |
99 | return connectDropTarget(_react2['default'].createElement(
100 | 'div',
101 | null,
102 | this.props.children,
103 | useResize ? _react2['default'].createElement(_ResizableHandleJs2['default'], { styles: style, left: resizeHandlePosition.left, top: resizeHandlePosition.top, parent: this.props.node }) : null
104 | ));
105 | }
106 | }]);
107 |
108 | return ResizeContainer;
109 | })(_react2['default'].Component);
110 |
111 | ;
112 |
113 | var collect = function collect(connect, monitor) {
114 | return {
115 | connectDropTarget: connect.dropTarget(),
116 | isOver: monitor.isOver(),
117 | canDrop: monitor.canDrop()
118 | };
119 | };
120 |
121 | // Export the wrapped component:
122 | exports['default'] = (0, _reactDnd.DropTarget)(_utilItemTypesJs2['default'].RESIZABLE_HANDLE, target, collect)(ResizeContainer);
123 | module.exports = exports['default'];
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-designer-core",
3 | "version": "1.4.1",
4 | "description": "React-designer-core is a set of core components for easy content creation.",
5 | "main": "lib/Designer.js",
6 | "author": "Roman Samec",
7 | "homepage": "https://github.com/rsamec/react-designer-core",
8 | "repository": {
9 | "type": "git",
10 | "url": "https://github.com/rsamec/react-designer-core.git"
11 | },
12 | "bugs": {
13 | "url": "https://github.com/rsamec/react-designer-core/issues"
14 | },
15 | "dependencies": {
16 | "classnames": "^2.2.3",
17 | "lodash": "^4.17.4",
18 | "prop-types": "^15.6.0",
19 | "react-dnd": "^2.5.3",
20 | "react-dnd-html5-backend": "^2.2.0",
21 | "react-treeview": "^0.4.7",
22 | "transhand": "^0.1.26"
23 | },
24 | "devDependencies": {
25 | "babel-eslint": "^4.1.3",
26 | "babel-runtime": "^6.6.1",
27 | "babelify": "^7.3.0",
28 | "draft-js": "^0.7.0",
29 | "draft-js-export-html": "^0.2.2",
30 | "eslint": "^1.6.0",
31 | "eslint-plugin-react": "^3.5.1",
32 | "freezer-js": "^0.13.0",
33 | "gulp": "^3.9.0",
34 | "gu": "^0.7.6",
35 | "react-overlays": "^0.8.1",
36 | "react-split-pane": "^0.1.66"
37 | },
38 | "peerDependencies": {
39 | "react": ">=15.0.0",
40 | "react-dom": ">=15.0.0"
41 | },
42 | "browserify-shim": {
43 | "react": "global:React"
44 | },
45 | "scripts": {
46 | "build": "gulp clean && NODE_ENV=production gulp build",
47 | "examples": "gulp dev:server",
48 | "lint": "eslint ./; true",
49 | "publish:site": "NODE_ENV=production gulp publish:examples",
50 | "release": "NODE_ENV=production gulp release",
51 | "start": "gulp dev",
52 | "test": "echo \"no tests yet\" && exit 0",
53 | "watch": "gulp watch:lib"
54 | },
55 | "keywords": [
56 | "react",
57 | "react-component"
58 | ]
59 | }
60 |
--------------------------------------------------------------------------------
/src/Designer.js:
--------------------------------------------------------------------------------
1 | import Workplace from './components/Workplace.js';
2 | import ObjectBrowser from './components/ObjectBrowser.js';
3 |
4 | export default {
5 | Workplace:Workplace,
6 | ObjectBrowser:ObjectBrowser
7 | };
8 |
--------------------------------------------------------------------------------
/src/components/ObjectBrowser.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import TreeView from 'react-treeview';
3 | import _ from 'lodash';
4 | import cx from 'classnames';
5 |
6 | const CONTAINER_KEYS = ["ObjectSchema","Container","Repeater","Grid","Cell","BackgroundContainer"];
7 |
8 | export default class ObjectBrowser extends React.Component {
9 | constructor(props) {
10 | super(props)
11 | this.state = {filterText: ''};
12 | }
13 | handleUserInput(e) {
14 | this.setState({
15 | filterText: e.target.value
16 | });
17 | }
18 | executeAction(action,args){
19 | if (action === "onDragStart"){
20 | this.currentItem = args;
21 | return;
22 | }
23 | if (action === "onDrop"){
24 | this.move(this.currentItem,args);
25 | this.currentItem = undefined;
26 | return;
27 | }
28 | }
29 | move(from, to){
30 | //console.log(from.node.name + " -> " + to.node.name);
31 | // transact returns a mutable object
32 | // to make all the local changes
33 |
34 | //find source
35 | var source = from.node;
36 | var isContainer = _.includes(CONTAINER_KEYS,source.elementName);
37 |
38 | //move source to target - do it in transaction
39 | var targetArray = isContainer? to.node.containers:to.node.boxes;
40 | targetArray.transact().push(from.node);
41 |
42 | //remove source - do it in transaction
43 | var sourceParent = isContainer?from.parentNode.containers:from.parentNode.boxes;
44 | var indexToRemove = sourceParent.indexOf(source);
45 | //console.log(indexToRemove);
46 | if (indexToRemove !== -1) {
47 | sourceParent.transact().splice(indexToRemove,1);
48 | }
49 |
50 | // all the changes are made at once
51 | targetArray.run();
52 | sourceParent.run();
53 |
54 | // use it as a normal array
55 | //trans[0] = 1000; // [1000, 1, 2, ..., 999]
56 | }
57 | render() {
58 | var classes = cx({
59 | 'node': true,
60 | 'selected': this.props.current.node === this.props.rootNode
61 | });
62 | let path = 'schema';
63 | return (
64 |
65 |
66 |
67 |
68 |
this.props.currentChanged(this.props.rootNode,path)}>{this.props.rootNode.name}
69 | {this.props.rootNode.containers.length === 0 ?
No objects to show. :
70 |
73 | }
74 |
75 | );
76 | }
77 | };
78 |
79 | class TreeNode extends React.Component
80 | {
81 | handleClick(node,path) {
82 | this.props.currentChanged(node,path);
83 | }
84 |
85 | //TODO: optimize -> now each node starts its own tree traversal
86 | hideNode(node,filterText){
87 | var trav = function(node){
88 | var containers = node.containers;
89 | var boxes = node.boxes;
90 | var anyBoxes = _.some(boxes,function(item){return item.name.toLowerCase().indexOf(filterText.toLowerCase()) !== -1});
91 | if (anyBoxes) return true;
92 | if (node.name.indexOf(filterText) !== -1) return true;
93 | //recursion condtion stop
94 | var childrenBoxes = false;
95 | for (var i in containers)
96 | {
97 | //recursion step
98 | childrenBoxes = trav(containers[i]);
99 | if (childrenBoxes) return true;
100 | }
101 | return false;
102 | };
103 |
104 | return !trav(node);
105 | }
106 | shouldComponentUpdate( nextProps ){
107 | return true;
108 | // The comparison is fast, and we won't render the component if
109 | // it does not need it. This is a huge gain in performance.
110 | //var current = this.props.current.node;
111 | //var nextCurrent = nextProps.current.node;
112 | //return this.props.filterText != nextProps.filterText || this.props.nodes != nextProps.nodes || (current!==undefined && nextCurrent !==undefined && current.name != nextCurrent.name);
113 | }
114 | render() {
115 | var containers = this.props.node.containers || [];
116 |
117 | return (
118 |
119 | {containers.map(function (node, i) {
120 | if (this.hideNode(node,this.props.filterText)) return;
121 |
122 | var onDragStart = function(e) {
123 | console.log('drag started');
124 | e.stopPropagation();
125 | var draggingItem = {
126 | node: node,
127 | parentNode : this.props.node};
128 | this.props.executeAction("onDragStart",draggingItem);
129 |
130 | }.bind(this);
131 | var onDragEnter = function(e){
132 | e.preventDefault(); // Necessary. Allows us to drop.
133 | e.stopPropagation();
134 | //window.event.returnValue=false;
135 | //if (this.props.dragging.type !== this.props.item.type || this.props.dragging.id !== this.props.item.id) {
136 | var dropCandidate = {node: node, parentNode:this.props.node};
137 | // var self = this;
138 | this.props.executeAction("dropPossible", dropCandidate);
139 | //}
140 | }.bind(this);
141 | var onDragOver = function(e){
142 | e.preventDefault(); // Necessary. Allows us to drop.
143 | e.stopPropagation();
144 | //window.event.returnValue=false;
145 | }.bind(this);
146 | var onDrop = function(e) {
147 | e.preventDefault();
148 | e.stopPropagation();
149 | var dropPlace= {node: node, parentNode: this.props.node};
150 | this.props.executeAction("onDrop",dropPlace);
151 | }.bind(this);
152 |
153 |
154 | var type = node.elementName;
155 |
156 | var containers = node.containers || [];
157 | var boxes = node.boxes || [];
158 |
159 | var selected = this.props.current.node === node;
160 | var parentSelected = this.props.current.parentNode === node;
161 | var path = `${this.props.path}.containers[${i}]`;
162 |
163 | var classes = cx({
164 | 'node': true,
165 | 'selected': selected,
166 | 'parentSelected':this.props.parentSelected
167 | });
168 |
169 | var label =
{node.name};
172 | return (
173 |
174 |
175 |
176 | {boxes.map(function (box, j) {
177 |
178 | var onDragStart1 = function(e) {
179 | console.log('drag started');
180 | e.stopPropagation();
181 | var draggingItem = {
182 | node: box,
183 | parentNode : node};
184 | this.props.executeAction("onDragStart",draggingItem);
185 |
186 | }.bind(this);
187 |
188 | if (box.name.toLowerCase().indexOf(this.props.filterText.toLowerCase()) === -1) {
189 | return;
190 | }
191 |
192 | var boxPath = `${path}.boxes[${j}]`;
193 | var classes = cx({
194 | 'node': true,
195 | 'selected': this.props.current.node === box
196 | });
197 | return ({box.name}
)
198 |
199 | },this)}
200 |
201 |
202 | );
203 | }, this)}
204 |
205 | );
206 | }
207 | }
208 |
--------------------------------------------------------------------------------
/src/components/Workplace.js:
--------------------------------------------------------------------------------
1 | var React = require('react');
2 | import PropTypes from 'prop-types';
3 | import _ from 'lodash';
4 | import HTML5Backend from 'react-dnd-html5-backend';
5 | import { DragDropContext } from 'react-dnd';
6 | import {CSSTranshand} from 'transhand';
7 | import Container from './../workplace/Container';
8 |
9 | import backgroundStyle from '../util/backgroundStyle';
10 |
11 | const DEFAULT_TRANSFORM = {
12 | tx: 0, ty: 0, //translate in px
13 | sx: 1, sy: 1, //scale
14 | rz: 0, //rotation in radian
15 | ox: 0.5, oy: 0.5 //transform origin
16 | };
17 |
18 | const DEFAULT_ROOT_PATH = 'path';
19 |
20 | class Workplace extends React.Component {
21 |
22 | constructor(props){
23 | super(props);
24 | this.state = {};
25 | }
26 | getChildContext() {
27 | return {snapGrid: this.props.snapGrid};
28 | }
29 | handleChange(change) {
30 | var currentNode = this.props.current.node;
31 | if (currentNode == undefined) return;
32 |
33 | var style = currentNode.style;
34 | if (style === undefined) return;
35 |
36 | //resolution strategy -> defaultTransform -> style.transform -> change
37 | var transform = _.merge(_.merge(_.clone(DEFAULT_TRANSFORM), style.transform), change);
38 |
39 |
40 | var updated = currentNode.set('style', _.extend(_.clone(style), {'transform': transform}));
41 | this.props.currentChanged(updated);
42 | }
43 |
44 |
45 | currentChanged(node,path,domEl) {
46 | if (this.props.currentChanged !== undefined) this.props.currentChanged(node,path);
47 | this.setState({
48 | currentDOMNode: domEl
49 | });
50 | }
51 |
52 | render() {
53 |
54 | const {schema,current,currentChanged, dataContext} = this.props;
55 |
56 | var handleClick = function () {
57 | if (currentChanged !== undefined) currentChanged(schema,DEFAULT_ROOT_PATH);
58 | };
59 |
60 |
61 |
62 | var style = current.node && current.node.style || {};
63 | var transform = _.merge(_.clone(DEFAULT_TRANSFORM), style.transform);
64 |
65 | var ctx = (schema.props && schema.props.context) || {};
66 | var customStyles = ctx['styles'] || {};
67 | var code = ctx['code'] && ctx['code'].compiled;
68 | var customCode = !!code ? eval(code) : undefined;
69 |
70 | //append shared code to data context
71 | if (dataContext !== undefined) dataContext.customCode = customCode;
72 |
73 | var context = {
74 | styles: customStyles,
75 | customCode: customCode
76 | };
77 |
78 |
79 | var bg = (schema.props && schema.props.background) || {};
80 | var bgStyle = backgroundStyle(bg);
81 |
82 | bgStyle.position = 'absolute';
83 | bgStyle.width = '100%';
84 | bgStyle.height = '100%';
85 | bgStyle.zIndex = -1;
86 |
87 | var component =
88 |
;
102 |
103 | return (
104 |
105 | {component}
106 | {this.state.currentDOMNode !== undefined ?
107 |
: null}
109 |
110 | );
111 | }
112 | };
113 | Workplace.childContextTypes = {snapGrid: PropTypes.arrayOf(PropTypes.number)};
114 | export default DragDropContext(HTML5Backend)(Workplace);
115 |
--------------------------------------------------------------------------------
/src/util/ItemTypes.js:
--------------------------------------------------------------------------------
1 | export default {
2 | BLOCK: 'block',
3 | IMAGE: 'image',
4 | BOX:'box',
5 | CONTAINER:'container',
6 | RESIZABLE_HANDLE:'resize',
7 | TREE_NODE:'treeNode'
8 | };
9 |
--------------------------------------------------------------------------------
/src/util/backgroundStyle.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 |
3 | export default function(source,panelSize) {
4 | var bg = source;
5 | var bgStyle = {};
6 | if (bg === undefined) return bgStyle;
7 |
8 | //size
9 | if (!!bg.size) {
10 | if (panelSize !== undefined && (bg.size === "leftHalf" || bg.size === "rightHalf")) {
11 | bgStyle.backgroundSize = `${panelSize.width * 2}px ${panelSize.height}px`;
12 | bgStyle.backgroundPosition = bg.size === "leftHalf" ? '0% 0%' : '100% 0%';
13 | //console.log(bgStyle);
14 | }
15 | else{
16 | bgStyle.backgroundSize = bg.size;
17 | if (!!bg.position) bgStyle.backgroundPosition = bg.position;
18 | }
19 | }
20 | //gradient
21 | var bgGradient = bg.gradient;
22 | if (bgGradient !== undefined) {
23 | //gradient
24 | if (bgGradient.stops !== undefined) {
25 | var gradientStops = _.reduce(bgGradient.stops, function (memo, stop) {
26 | return memo + ', ' + stop.color + ' ' + (stop.offset * 100) + '%'
27 | }, '');
28 |
29 | var orientation = bgGradient.orientation || 'top';
30 | var grandientType = bgGradient.orientation === 'center, ellipse cover' ? '-webkit-radial-gradient' : '-webkit-linear-gradient';
31 | bgStyle.background = grandientType + '(' + orientation + gradientStops + ')';
32 | }
33 | }
34 |
35 | //color
36 | var bgColor = bg.color;
37 | if (bgColor !== undefined) {
38 | if (!!bgColor.color) bgStyle.backgroundColor = bgColor.color;
39 | if (!!bgColor.alpha) bgStyle.opacity = bgColor.alpha / 100;
40 | }
41 |
42 | if (!!bg.image) bgStyle.backgroundImage = 'url(' + bg.image + ')';
43 | if (!!bg.repeat) bgStyle.backgroundRepeat = bg.repeat;
44 | if (!!bg.attachment) bgStyle.backgroundAttachment = bg.attachment;
45 |
46 | var filter = bg.filter || {};
47 | var cssFilter = "";
48 | if (!!filter.blur) cssFilter += ' blur(' + filter.blur + 'px)';
49 | if (!!filter.brightness) cssFilter += ' brightness(' + filter.brightness + '%)';
50 | if (!!filter.contrast) cssFilter += ' contrast(' + filter.contrast + '%)';
51 | if (!!filter.grayscale) cssFilter += ' grayscale(' + filter.grayscale + '%)';
52 | if (!!filter.hueRotate) cssFilter += ' hue-rotate(' + filter.hueRotate + 'deg)';
53 | if (!!filter.invert) cssFilter += ' invert(' + filter.invert + '%)';
54 | if (!!filter.opacity) cssFilter += ' opacity(' + filter.opacity + '%)';
55 | if (!!filter.saturate) cssFilter += ' saturate(' + filter.saturate + '%)';
56 | if (!!filter.sepia) cssFilter += ' sepia(' + filter.sepia + '%)';
57 |
58 | if (!!cssFilter) {
59 | bgStyle.WebkitFilter = cssFilter;
60 | bgStyle.filter = cssFilter;
61 | }
62 | //bgStyle.position = 'absolute';
63 | return bgStyle;
64 | }
65 |
--------------------------------------------------------------------------------
/src/util/generateCssTransform.js:
--------------------------------------------------------------------------------
1 | export default function generateCssTransform(transform) {
2 | var cssTransform = '';
3 |
4 | if (transform.tx !== undefined) cssTransform += ' translateX(' + transform.tx + 'px)';
5 | if (transform.ty !== undefined) cssTransform += ' translateY(' + transform.ty + 'px)';
6 | if (transform.rz !== undefined) cssTransform += ' rotate(' + transform.rz + 'rad)';
7 | if (transform.sx !== undefined) cssTransform += ' scaleX(' + transform.sx + ')';
8 | if (transform.sy !== undefined) cssTransform += ' scaleY(' + transform.sy + ')';
9 |
10 | return cssTransform
11 | }
12 |
--------------------------------------------------------------------------------
/src/workplace/Box.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import ReactDOM from 'react-dom';
4 | import { DragSource } from 'react-dnd';
5 | import _ from 'lodash';
6 | import cx from 'classnames';
7 |
8 | import ResizeContainer from './ResizeContainer.js';
9 |
10 | import ItemTypes from '../util/ItemTypes.js';
11 | import generateCssTransform from '../util/generateCssTransform';
12 |
13 | /**
14 | * Implements the drag source contract.
15 | */
16 | const source = {
17 | beginDrag(props, monitor, component) {
18 | return {
19 | //effectAllowed: DropEffects.MOVE,
20 | item: component.props
21 | };
22 | }
23 | };
24 |
25 | /**
26 | * Specifies the props to inject into your component.
27 | */
28 | function collect(connect, monitor) {
29 | return {
30 | connectDragSource: connect.dragSource(),
31 | isDragging: monitor.isDragging()
32 | };
33 | }
34 |
35 | const propTypes = {
36 | // Injected by React DnD:
37 | isDragging: PropTypes.bool.isRequired,
38 | connectDragSource: PropTypes.func.isRequired
39 | };
40 |
41 | class Box extends React.Component {
42 |
43 |
44 | handleDoubleClick(e) {
45 | e.stopPropagation();
46 | if (this.props.currentChanged !== undefined) this.props.currentChanged(this.props.node, this.props.path, ReactDOM.findDOMNode(this));
47 | }
48 |
49 | handleClick(e) {
50 | e.stopPropagation();
51 | if (this.props.currentChanged !== undefined) this.props.currentChanged(this.props.node,this.props.path);
52 | }
53 |
54 | shouldComponentUpdate(nextProps) {
55 |
56 | // The comparison is fast, and we won't render the component if
57 | // it does not need it. This is a huge gain in performance.
58 | var box = this.props.node;
59 | var update = box !== nextProps.node || this.props.selected != nextProps.selected;
60 | //console.log(nextProps.node.name + ' : ' + update);
61 | if (update) return update;
62 |
63 | //test -> widget custom style changed
64 | var propsStyles = this.props.ctx.styles;
65 | var nextPropsStyles = nextProps.ctx.styles;
66 | update = (propsStyles && propsStyles[box.elementName]) !== (nextPropsStyles && nextPropsStyles[box.elementName]);
67 |
68 | return update;
69 | }
70 | // componentWillReceiveProps(nextProps){
71 | // var index = this.props.index;
72 | // for (var action of ['right','left','up','down'])
73 | // {
74 | // if (nextProps.selected)this.props.bindShortcut(action, this.props.moveBox.bind(this,index,action));//: this.props.unbindShortcut(action);
75 | // }
76 | // }
77 | render() {
78 |
79 | const {widgets,widgetRenderer,selected,node,dataBinder, currentChanged, index} = this.props;
80 | const {isDragging, connectDragSource, item } = this.props;
81 |
82 | //prepare styles
83 | var classes = cx({
84 | 'box': true,
85 | 'selected': selected
86 | });
87 |
88 | //clone node
89 | var box = node.toJS();
90 |
91 | //document custom style
92 | var ctx = this.props.ctx || {};
93 | var customStyle = ctx["styles"] && ctx["styles"][box.elementName];
94 |
95 | //specific props resolution rule -> propagate width and height from style to widget props
96 | var boxProps = box.props || {};
97 | var boxStyle = box.style || {};
98 | if (!boxProps.width && !!boxStyle.width) boxProps.width =boxStyle.width;
99 | if (!boxProps.height && !!boxStyle.height) boxProps.height = boxStyle.height;
100 |
101 |
102 | var boxComponent = widgetRenderer !== undefined && widgets !== undefined ?
103 | React.createElement(widgetRenderer,{
104 | tabIndex: index,
105 | widget:widgets[box.elementName],
106 | node:box,
107 | dataBinder:dataBinder,
108 | customStyle:customStyle,
109 | customCode:ctx['customCode'],
110 | designer:true,
111 | current:node,
112 | currentChanged:currentChanged,
113 | selected:selected
114 | },null) :
115 |
No widget renderer or widget factory provided.
;
116 |
117 |
118 | //create style
119 | var styles = _.extend({position:this.props.position},boxStyle);
120 | if (boxStyle.transform !== undefined) styles['transform'] = generateCssTransform(boxStyle.transform);
121 |
122 | //wrap with div double click for transhand transformation
123 | if (box.elementName !== "Core.RichTextContent") boxComponent =
{boxComponent}
;
124 |
125 | return connectDragSource(
126 |
127 |
128 | {boxComponent}
129 |
130 |
131 | );
132 | }
133 | };
134 |
135 | Box.propTypes = propTypes;
136 | // Export the wrapped component:
137 | export default DragSource(ItemTypes.BOX, source, collect)(Box);
138 |
--------------------------------------------------------------------------------
/src/workplace/Container.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import ItemTypes from '../util/ItemTypes.js';
4 | import { DropTarget } from 'react-dnd';
5 |
6 |
7 | import _ from 'lodash';
8 | import cx from 'classnames';
9 |
10 | import Box from './Box';
11 | import ResizableHandle from './ResizableHandle.js';
12 |
13 | const HANDLE_OFFSET = 8;
14 |
15 | let snapToGrid = function (grid, deltaX, deltaY) {
16 | let x = Math.round(deltaX / grid[0]) * grid[0];
17 | let y = Math.round(deltaY / grid[1]) * grid[1];
18 | return [x, y];
19 | }
20 |
21 |
22 | const target = {
23 | drop(props, monitor, component) {
24 | if (monitor.didDrop()) {
25 | // If you want, you can check whether some nested
26 | // target already handled drop
27 | return;
28 | }
29 |
30 | var item = monitor.getItem().item;
31 |
32 | var delta = monitor.getDifferenceFromInitialOffset();
33 |
34 | if (!!!delta) return;
35 |
36 | if (monitor.getItemType() === ItemTypes.BOX) {
37 | var left = Math.round(isNaN(item.left) ? 0 : parseInt(item.left, 10) + delta.x);
38 | var top = Math.round(isNaN(item.top) ? 0 : parseInt(item.top, 10) + delta.y);
39 |
40 | component.moveBox(item.index, left, top);
41 | }
42 |
43 | if (monitor.getItemType() === ItemTypes.RESIZABLE_HANDLE) {
44 | var left = Math.round(delta.x < 0 ? delta.x + HANDLE_OFFSET : delta.x - HANDLE_OFFSET);
45 | var top = Math.round(delta.y < 0 ? delta.y + HANDLE_OFFSET : delta.y - HANDLE_OFFSET);
46 | component.resizeContainer(item.parent, left, top);
47 | }
48 | }
49 | };
50 |
51 | class Container extends React.Component {
52 | shouldComponentUpdate(nextProps, nextState) {
53 |
54 | // The comparison is fast, and we won't render the component if
55 | // it does not need it. This is a huge gain in performance.
56 | var node = this.props.node;
57 | var current = this.props.current;
58 | var update = node !== nextProps.node || (current && current.path) != (nextProps.current && nextProps.current.path);
59 |
60 | if (update) return update;
61 |
62 | //test -> container custom style changed
63 | var propsStyles = this.props.ctx.styles;
64 | var nextPropsStyles = nextProps.ctx.styles;
65 | update = (propsStyles !== nextPropsStyles);
66 |
67 | return update;
68 | }
69 |
70 | moveBox(index, left, top) {
71 | var deltas = snapToGrid(this.context.snapGrid, left, top);
72 | this.moveBoxEx(index, deltas[0], deltas[1]);
73 | }
74 | moveBoxEx(index, left, top) {
75 | var boxes = this.props.boxes;
76 | if (boxes === undefined) return;
77 | var box = boxes[index];
78 | if (box === undefined) return;
79 |
80 | var updated = box.set({ 'style': _.merge(_.clone(box.style), { 'left': left, 'top': top }) });
81 | this.props.currentChanged(updated);
82 | }
83 | // moveBoxToDirection(index, direction) {
84 | // var boxes = this.props.boxes;
85 | // if (boxes === undefined) return;
86 | // var box = boxes[index];
87 | // if (box === undefined) return;
88 |
89 | // var deltas = this.getDirectionDeltas(direction);
90 | // var updated = box.set({ 'style': _.merge(_.clone(box.style), { 'left': (box.style.left || 0) + deltas[0], 'top': (box.style.top || 0) + deltas[1], }) });
91 | // this.props.currentChanged(updated);
92 | // }
93 | getDirectionDeltas(direction) {
94 | var snaps = this.context.snapGrid;
95 | var deltas = [0, 0]
96 | switch (direction) {
97 | case "left":
98 | return [-1 * snaps[0], 0];
99 | case "right":
100 | return [snaps[0], 0];
101 | case "up":
102 | return [0, -1 * snaps[1]];
103 | case "down":
104 | return [0, snaps[1]];
105 | default:
106 | return deltas;
107 | }
108 | }
109 |
110 | resizeContainer(container, deltaWidth, deltaHeight) {
111 | if (container === undefined) return;
112 |
113 | //TODO: use merge instead of clone
114 | var style = _.clone(container.style) || {};
115 | var newWidth = (style.width || 0) + deltaWidth;
116 | if (newWidth < 0) return;
117 | var newHeight = (style.height || 0) + deltaHeight;
118 | if (newHeight < 0) return;
119 |
120 | var deltas = snapToGrid(this.context.snapGrid, newWidth, newHeight);
121 | style.width = deltas[0];
122 | style.height = deltas[1];
123 |
124 | var updated = container.set({ 'style': style });
125 | this.props.currentChanged(updated);
126 |
127 | }
128 |
129 | handleClick(e) {
130 | e.stopPropagation();
131 | if (this.props.handleClick !== undefined) this.props.handleClick();
132 | }
133 |
134 | render() {
135 | let { elementName, ctx, widgets, widgetRenderer, current, currentChanged, node, parent, dataBinder } = this.props;
136 | const { canDrop, isOver, connectDropTarget } = this.props;
137 |
138 | var containers = this.props.containers || [];
139 | var boxes = this.props.boxes || [];
140 |
141 | //styles
142 | var classes = cx({
143 | 'con': true,
144 | 'selected': this.props.selected,
145 | 'parentSelected': this.props.parentSelected,
146 | 'root': this.props.isRoot
147 | });
148 |
149 | var styles = {
150 | left: this.props.left,
151 | top: this.props.top,
152 | height: this.props.height,
153 | width: this.props.width,
154 | position: this.props.position || 'relative'
155 | };
156 |
157 |
158 | var nodeProps = node.props;
159 | var nodeBindings = node.bindings || {};
160 |
161 | //apply custom styles
162 | var customStyle = ctx["styles"] && ctx["styles"][elementName];
163 | if (customStyle !== undefined) nodeProps = _.merge(_.cloneDeep(customStyle), nodeProps);
164 |
165 | //apply node props
166 | if (dataBinder !== undefined && widgetRenderer) nodeProps = widgetRenderer.bindProps(_.cloneDeep(nodeProps), nodeBindings.bindings, dataBinder, true);
167 |
168 |
169 | var containerComponent = widgets[elementName] || 'div';
170 |
171 | return connectDropTarget(
172 |
173 |
174 | {containers.length !== 0 ? React.createElement(containerComponent, nodeProps, containers.map(function (container, index) {
175 |
176 | var selected = container === current.node;
177 | var parentSelected = false; //container === current.parentNode;
178 | var key = container.name + index;
179 | var containerStyle = container.style || {};
180 |
181 | var path = `${this.props.path}.containers[${index}]`;
182 |
183 | var handleClick = function () {
184 | if (currentChanged !== undefined) currentChanged(container, path);
185 | }
186 |
187 | var left = containerStyle.left === undefined ? 0 : parseInt(containerStyle.left, 10);
188 | var top = containerStyle.top === undefined ? 0 : parseInt(containerStyle.top, 10);
189 |
190 |
191 | var childProps = _.cloneDeep(container.props) || {};
192 | var childBindings = container.bindings || {};
193 |
194 | //apply custom styles
195 | var childCustomStyle = ctx["styles"] && ctx["styles"][container.elementName];
196 | if (childCustomStyle !== undefined) childProps = _.merge(_.cloneDeep(childCustomStyle), childProps);
197 |
198 | //apply node props
199 | if (dataBinder !== undefined && widgetRenderer) childProps = widgetRenderer.bindProps(childProps, childBindings.bindings, dataBinder, true);
200 |
201 |
202 | //specific props resolution rule -> propagate width and height from style to child container props
203 |
204 | if (!childProps.width && !!containerStyle.width) childProps.width = containerStyle.width;
205 | if (!childProps.height && !!containerStyle.height) childProps.height = containerStyle.height;
206 | if (!childProps.left && !!containerStyle.left) childProps.left = containerStyle.left;
207 | if (!childProps.top && !!containerStyle.top) childProps.top = containerStyle.top;
208 |
209 | var applyDirectChildContainers = elementName == "Grid";//container.containers && container.containers.length === 0;
210 | //var childComponent = 'div';
211 | let wrappedContainer = ;
233 |
234 | return applyDirectChildContainers ? (React.createElement(widgets[container.elementName] || 'div', _.extend(childProps, { child: true, key: key }), wrappedContainer)) : wrappedContainer;
235 |
236 | }, this)) : null}
237 |
238 | {boxes.map(function (box, index) {
239 |
240 | var selected = box === current.node;
241 | var key = box.name + index;
242 |
243 | var boxStyle = box.style || {};
244 | var left = boxStyle.left === undefined ? 0 : parseInt(box.style.left, 10);
245 | var top = boxStyle.top === undefined ? 0 : parseInt(box.style.top, 10);
246 |
247 | var path = `${this.props.path}.boxes[${index}]`;
248 |
249 | var box =
263 |
264 |
265 | return box;
266 | }, this)
267 | }
268 |
269 | {this.props.isRoot || (this.props.width === undefined || this.props.height === undefined) ? null :
270 |
271 | }
272 |
273 | );
274 | }
275 | }
276 |
277 | Container.contextTypes = {
278 | snapGrid: PropTypes.arrayOf(PropTypes.number)
279 | }
280 |
281 | var collect = (connect, monitor) => ({
282 | connectDropTarget: connect.dropTarget(),
283 | isOver: monitor.isOver(),
284 | canDrop: monitor.canDrop()
285 | });
286 | var WrappedContainer = DropTarget([ItemTypes.RESIZABLE_HANDLE, ItemTypes.BOX], target, collect)(Container);
287 | export default WrappedContainer;
288 |
--------------------------------------------------------------------------------
/src/workplace/ResizableHandle.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { DragSource } from 'react-dnd';
4 | import ItemTypes from '../util/ItemTypes.js';
5 |
6 | /**
7 | * Implements the drag source contract.
8 | */
9 | const source = {
10 | beginDrag(props, monitor, component) {
11 | return {
12 | //effectAllowed: DropEffects.MOVE,
13 | item: component.props
14 | };
15 | }
16 | };
17 |
18 | /**
19 | * Specifies the props to inject into your component.
20 | */
21 | function collect(connect, monitor) {
22 | return {
23 |
24 | connectDragSource: connect.dragSource(),
25 | isDragging: monitor.isDragging()
26 | };
27 | }
28 | const propTypes = {
29 | //item: PropTypes.isRequired,
30 |
31 |
32 | // Injected by React DnD:
33 | isDragging: PropTypes.bool.isRequired,
34 | connectDragSource: PropTypes.func.isRequired
35 | };
36 |
37 | class ResizableHandle extends React.Component {
38 | render() {
39 | const { isDragging, connectDragSource, item } = this.props;
40 | return connectDragSource(
41 |
42 |
43 | );
44 | }
45 | };
46 |
47 | ResizableHandle.propTypes = propTypes;
48 |
49 | // Export the wrapped component:
50 | export default DragSource(ItemTypes.RESIZABLE_HANDLE, source, collect)(ResizableHandle);
51 |
52 |
--------------------------------------------------------------------------------
/src/workplace/ResizeContainer.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import ItemTypes from '../util/ItemTypes.js';
4 | import { DropTarget } from 'react-dnd';
5 |
6 | import _ from 'lodash';
7 | import ResizableHandle from './ResizableHandle.js';
8 |
9 |
10 | const target = {
11 | drop(props, monitor, component) {
12 | if (monitor.didDrop()) {
13 | // If you want, you can check whether some nested
14 | // target already handled drop
15 | return;
16 | }
17 |
18 | var item = monitor.getItem().item;
19 |
20 | var delta = monitor.getDifferenceFromInitialOffset();
21 |
22 | if (!!!delta) return;
23 |
24 | if (monitor.getItemType() === ItemTypes.RESIZABLE_HANDLE) {
25 | var left = Math.round(delta.x);
26 | var top = Math.round(delta.y);
27 |
28 | component.resizeContainer(item.parent, left, top);
29 | };
30 |
31 | }
32 | };
33 | const HANDLE_OFFSET = 30;
34 |
35 | class ResizeContainer extends React.Component {
36 | resizeContainer(container, deltaWidth, deltaHeight) {
37 | if (container === undefined) return;
38 |
39 | //TODO: use merge instead of clone
40 | var style = _.cloneDeep(container.style);
41 | style.width += deltaWidth;
42 | style.height += deltaHeight;
43 |
44 | var updated = container.set({'style': style});
45 | this.props.currentChanged(updated);
46 |
47 | }
48 | render() {
49 |
50 | const { canDrop, isOver, connectDropTarget} = this.props;
51 |
52 | var style = this.props.node && this.props.node.style || {};
53 |
54 | //resize handle position
55 | var useResize = !!style.height && !!style.width;
56 | var resizeHandlePosition = {top: (style.height - HANDLE_OFFSET), left: (style.width - HANDLE_OFFSET)};
57 |
58 |
59 | return connectDropTarget(
60 |
61 | {this.props.children}
62 | {useResize?
63 | :null
64 | }
65 |
66 | );
67 | }
68 | };
69 |
70 | var collect = (connect, monitor) => ({
71 | connectDropTarget: connect.dropTarget(),
72 | isOver: monitor.isOver(),
73 | canDrop: monitor.canDrop()
74 | });
75 |
76 | // Export the wrapped component:
77 | export default DropTarget(ItemTypes.RESIZABLE_HANDLE, target, collect)(ResizeContainer);
78 |
--------------------------------------------------------------------------------