diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/PageTree/PageTree.js b/typo3/sysext/backend/Resources/Public/JavaScript/PageTree/PageTree.js index bd247ae34ee6b06c4cdb168af6f4da1959e68b79..23259dc54a34dfa7f683ed170e7925fd4d3a288f 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/PageTree/PageTree.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/PageTree/PageTree.js @@ -412,7 +412,7 @@ define(['jquery', .append('input') .attr('class', 'node-edit') .style('top', function () { - var top = node.y + 15; //svg margin top + var top = node.y + _this.settings.marginTop; return top + 'px'; }) .style('left', (node.x + _this.textPosition + 5) + 'px') diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/PageTree/PageTreeDragDrop.js b/typo3/sysext/backend/Resources/Public/JavaScript/PageTree/PageTreeDragDrop.js index d07b8f1511d1770e20badd0def672b7cce0735b3..31e4a10f4c109398ab7d6a035bca990004e7fc8b 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/PageTree/PageTreeDragDrop.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/PageTree/PageTreeDragDrop.js @@ -173,8 +173,10 @@ define([ tree.settings.nodeDragPosition = false; + _this.openNodeTimeout(); + if (node.isOver - || (tree.settings.nodeOver.node && tree.settings.nodeOver.node.parentsUid.indexOf(node.stateIdentifier) !== -1) + || (tree.settings.nodeOver.node && tree.settings.nodeOver.node.parentsStateIdentifier.indexOf(node.stateIdentifier) !== -1) || !tree.isOverSvg) { _this.addNodeDdClass({ $nodeDd: $nodeDd, $nodesWrap: $nodesWrap, className: 'nodrop' }); @@ -250,7 +252,7 @@ define([ if ( !(node.isOver - || (tree.settings.nodeOver.node && tree.settings.nodeOver.node.parentsUid.indexOf(node.stateIdentifier) !== -1) + || (tree.settings.nodeOver.node && tree.settings.nodeOver.node.parentsStateIdentifier.indexOf(node.stateIdentifier) !== -1) || !tree.settings.canNodeDrag || !tree.isOverSvg ) @@ -332,6 +334,33 @@ define([ .on('end', self.dragEnd); }, + /** + * Open node with children while holding the node/element over this node for one second + */ + openNodeTimeout: function () { + var _this = this; + + if (!_this.timeout) { + _this.timeout = {} + } + + if (_this.tree.settings.nodeOver.node.hasChildren && !_this.tree.settings.nodeOver.node.expanded) { + if (_this.timeout.node != _this.tree.settings.nodeOver.node) { + _this.timeout.node = _this.tree.settings.nodeOver; + clearTimeout(_this.timeout.time); + _this.timeout.time = setTimeout(function () { + if (_this.tree.settings.nodeOver.node) { + _this.tree.showChildren(_this.tree.settings.nodeOver.node); + _this.tree.prepareDataForVisibleNodes(); + _this.tree.update(); + } + }, 1000); + } + } else { + clearTimeout(_this.timeout.time); + } + }, + changeNodeClasses: function () { var elementNodeBg = this.tree.svg.select('.node-over'); var $svg = $(this.tree.svg.node()); @@ -340,7 +369,7 @@ define([ var nodeBgBorder = this.tree.nodesBgContainer.selectAll('.node-bg__border'); if (elementNodeBg.size() && this.tree.isOverSvg) { - //line between nodes + // line between nodes if (nodeBgBorder.empty()) { nodeBgBorder = this.tree.nodesBgContainer .append('rect') @@ -382,7 +411,7 @@ define([ nodeBgBorder .style('display', 'none'); - if (this.tree.settings.nodeOver.node.open && this.tree.settings.nodeOver.node.hasChildren) { + if (this.tree.settings.nodeOver.node.expanded && this.tree.settings.nodeOver.node.hasChildren) { this.addNodeDdClass({ $nodeDd: $nodeDd, $nodesWrap: $nodesWrap, @@ -550,6 +579,8 @@ define([ top += d3.event.sourceEvent.pageY; } + _this.openNodeTimeout(); + $(document).find('.node-dd').css({ left: left, top: top, @@ -652,10 +683,10 @@ define([ var data = { node: tree.settings.nodeDrag, - uid: uid, //dragged node id - target: target, //hovered node - position: position, //before, in, after - command: options.command, //element is copied or moved + uid: uid, // dragged node id + target: target, // hovered node + position: position, // before, in, after + command: options.command, // element is copied or moved }; $.extend(data, options); @@ -703,8 +734,16 @@ define([ var index = _this.tree.nodes.indexOf(target); var newNode = {}; var removeNode = function (newNode) { - _this.tree.nodes.splice(_this.tree.nodes.indexOf(newNode), 1); - _this.tree.setParametersNode(_this.tree.nodes); + var index = _this.tree.nodes.indexOf(newNode); + + // if newNode is only one child + if (_this.tree.nodes[index - 1].depth != newNode.depth + && (!_this.tree.nodes[index + 1] || _this.tree.nodes[index + 1].depth != newNode.depth)) { + _this.tree.nodes[index - 1].hasChildren = false; + } + + _this.tree.nodes.splice(index, 1); + _this.tree.setParametersNode(); _this.tree.prepareDataForVisibleNodes(); _this.tree.update(); _this.tree.removeEditedText(); @@ -715,15 +754,19 @@ define([ newNode.identifier = -1; newNode.target = target; newNode.parents = target.parents; - newNode.parentsUid = target.parentsUid; + newNode.parentsStateIdentifier = target.parentsStateIdentifier; newNode.depth = target.depth; newNode.position = options.position; newNode.name = (typeof options.title !== 'undefined') ? options.title : TYPO3.lang['tree.defaultPageTitle']; + newNode.y = newNode.y || newNode.target.y; + newNode.x = newNode.x || newNode.target.x; if (options.position === 'in') { newNode.depth++; - _this.tree.nodes[index].open = true; + newNode.parents.unshift(index); + newNode.parentsStateIdentifier.unshift(_this.tree.nodes[index].stateIdentifier); _this.tree.nodes[index].hasChildren = true; + _this.tree.showChildren(_this.tree.nodes[index]); } if (options.position === 'in' || options.position === 'after') { @@ -741,21 +784,17 @@ define([ } _this.tree.nodes.splice(index, 0, newNode); - _this.tree.setParametersNode(_this.tree.nodes); + _this.tree.setParametersNode(); _this.tree.prepareDataForVisibleNodes(); _this.tree.update(); - _this.tree.removeEditedText(); _this.tree.nodeIsEdit = true; d3.select(_this.tree.svg.node().parentNode) .append('input') .attr('class', 'node-edit') - .style('top', function () { - var top = newNode.y + 15; //svg margin top - return top + 'px'; - }) - .style('left', (newNode.x + _this.tree.textPosition + 5) + 'px') + .style('top', newNode.y + _this.tree.settings.marginTop + 'px') + .style('left', newNode.x + _this.tree.textPosition + 5 + 'px') .style('width', _this.tree.settings.width - (newNode.x + _this.tree.textPosition + 20) + 'px') .style('height', _this.tree.settings.nodeHeight + 'px') .attr('text', 'text') @@ -763,7 +802,7 @@ define([ .on('keydown', function () { var code = d3.event.keyCode; - if (code === 13 || code === 9) { //enter || tab + if (code === 13 || code === 9) { // enter || tab _this.tree.nodeIsEdit = false; var newName = this.value.trim(); @@ -774,7 +813,7 @@ define([ } else { removeNode(newNode); } - } else if (code === 27) { //esc + } else if (code === 27) { // esc _this.tree.nodeIsEdit = false; removeNode(newNode); } diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/PageTree/PageTreeToolbar.js b/typo3/sysext/backend/Resources/Public/JavaScript/PageTree/PageTreeToolbar.js index 060ceec5259e8093d899dbecce08b3cb4019d67c..705d94b5986464f2ac555a17e7ed92c401e32bee 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/PageTree/PageTreeToolbar.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/PageTree/PageTreeToolbar.js @@ -204,16 +204,16 @@ define(['jquery', var _this = this; var name = $(input).val(); - this.tree.nodes[0].open = false; + this.tree.nodes[0].expanded = false; this.tree.nodes.forEach(function (node) { var regex = new RegExp(name, 'i'); if (regex.test(node.name) || regex.test(node.alias)) { _this.showParents(node); - node.open = true; + node.expanded = true; node.hidden = false; } else if (node.depth !== 0) { node.hidden = true; - node.open = false; + node.expanded = false; } }); @@ -235,11 +235,11 @@ define(['jquery', this.tree.nodes.forEach(function (node) { if (node.checked) { _this.showParents(node); - node.open = true; + node.expanded = true; node.hidden = false; } else { node.hidden = true; - node.open = false; + node.expanded = false; } }); } else { @@ -267,7 +267,7 @@ define(['jquery', parent.hidden = false; //expand parent node - parent.open = true; + parent.expanded = true; this.showParents(parent); }; diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/SvgTree.js b/typo3/sysext/backend/Resources/Public/JavaScript/SvgTree.js index 6cce08275c0bedd36bd896345c5cdb410d84cb54..448fcfda20b2731bbbd64131f4d76261f2557c7f 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/SvgTree.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/SvgTree.js @@ -37,6 +37,7 @@ define( showCheckboxes: false, showIcons: false, allowRecursiveDelete: false, + marginTop: 15, nodeHeight: 20, indentWidth: 16, width: 300, @@ -283,29 +284,41 @@ define( } var nodes = Array.isArray(json) ? json : []; - _this.setParametersNode(nodes); - _this.dispatch.call('loadDataAfter', _this); - _this.prepareDataForVisibleNodes(); - _this.nodesContainer.selectAll('.node').remove(); - _this.nodesBgContainer.selectAll('.node-bg').remove(); - _this.linksContainer.selectAll('.link').remove(); - _this.update(); + _this.replaceData(nodes); _this.nodesRemovePlaceholder(); }); }, /** - * Set parameters like node parents, parentsUid, checked + * Delete old tree and create new one + * + * @param {Node[]} nodes + */ + replaceData: function (nodes) { + var _this = this; + + _this.setParametersNode(nodes); + _this.dispatch.call('loadDataAfter', _this); + _this.prepareDataForVisibleNodes(); + _this.nodesContainer.selectAll('.node').remove(); + _this.nodesBgContainer.selectAll('.node-bg').remove(); + _this.linksContainer.selectAll('.link').remove(); + _this.update(); + }, + + /** + * Set parameters like node parents, parentsStateIdentifier, checked * * @param {Node[]} nodes */ setParametersNode: function (nodes) { var _this = this; + nodes = nodes || this.nodes; nodes = nodes.map(function (node, index) { - node.open = (_this.settings.expandUpToLevel !== null) ? node.depth < _this.settings.expandUpToLevel : Boolean(node.expanded); + node.expanded = (_this.settings.expandUpToLevel !== null) ? node.depth < _this.settings.expandUpToLevel : Boolean(node.expanded); node.parents = []; - node.parentsUid = []; + node.parentsStateIdentifier = []; node._isDragged = false; if (node.depth > 0) { var currentDepth = node.depth; @@ -313,12 +326,12 @@ define( var currentNode = nodes[i]; if (currentNode.depth < currentDepth) { node.parents.push(i); - node.parentsUid.push(nodes[i].stateIdentifier); + node.parentsStateIdentifier.push(nodes[i].stateIdentifier); currentDepth = currentNode.depth; } } } else if (node.hasChildren) { - node.open = true; + node.expanded = true; } // create stateIdentifier if doesn't exist (for category tree) @@ -347,7 +360,7 @@ define( nodesAddPlaceholder: function (node) { if (node) { - $('.svg-tree').find('.node-loader').css({ top: node.y + 15 }).show(); + $('.svg-tree').find('.node-loader').css({ top: node.y + this.settings.marginTop }).show(); } else { $('.svg-tree').find('.svg-tree-loader').show(); } @@ -363,7 +376,7 @@ define( var blacklist = {}; this.nodes.map(function (node, index) { - if (!node.open) { + if (!node.expanded) { blacklist[index] = true; } }); @@ -424,7 +437,7 @@ define( icon: '', }; Icons.getIcon(iconName, Icons.sizes.small, null, null, 'inline').done(function (icon) { - _this.data.icons[iconName].icon = icon.match(/<svg.*<\/svg>/im)[0]; + _this.data.icons[iconName].icon = icon.match(/<svg.*<\/svg>/is)[0]; if (update) { _this.update(); @@ -467,7 +480,7 @@ define( .attr('class', function (node, i) { return _this.getNodeBgClass(node, i, nodeBgClass); }) - .attr('style', function (node, i) { + .attr('style', function (node) { return node.backgroundColor ? 'fill: ' + node.backgroundColor + ';' : ''; }); @@ -486,6 +499,10 @@ define( .style('fill', this.getChevronColor) .attr('class', this.getChevronClass); + nodes + .select('.toggle') + .attr('visibility', this.getToggleVisibility); + if (this.settings.showIcons) { nodes .select('use.node-icon') @@ -661,7 +678,9 @@ define( .attr('class', 'toggle') .attr('visibility', this.getToggleVisibility) .attr('transform', 'translate(-8, -8)') - .on('click', _this.chevronClick.bind(this)); + .on('click', function (node) { + _this.chevronClick(node); + }); // improve usability by making the click area a 16px square chevron @@ -864,7 +883,7 @@ define( * @returns {String} */ getChevronTransform: function (node) { - return node.open ? 'translate(16,0) rotate(90)' : ' rotate(0)'; + return node.expanded ? 'translate(16,0) rotate(90)' : ' rotate(0)'; }, /** @@ -874,7 +893,7 @@ define( * @returns {String} */ getChevronColor: function (node) { - return node.open ? '#000' : '#8e8e8e'; + return node.expanded ? '#000' : '#8e8e8e'; }, /** @@ -894,7 +913,7 @@ define( * @returns {String} */ getChevronClass: function (node) { - return 'chevron ' + (node.open ? 'expanded' : 'collapsed'); + return 'chevron ' + (node.expanded ? 'expanded' : 'collapsed'); }, /** @@ -1066,7 +1085,7 @@ define( * @param {Node} node */ chevronClick: function (node) { - if (node.open) { + if (node.expanded) { this.hideChildren(node); } else { this.showChildren(node); @@ -1082,7 +1101,7 @@ define( * @param {Node} node */ hideChildren: function (node) { - node.open = false; + node.expanded = false; }, /** @@ -1091,7 +1110,7 @@ define( * @param {Node} node */ showChildren: function (node) { - node.open = true; + node.expanded = true; }, /**