Skip to content
Snippets Groups Projects
Commit 9243e4d6 authored by Andreas Nedbal's avatar Andreas Nedbal
Browse files

[BUGFIX] Properly show indeterminate state of categories

Prepared nodes were not properly handled in the category
element, causing the  indetermination state recalculation to
never trigger properly. Using the nodes from the
nodes-prepared event fixes this issue.

Resolves: #98689
Releases: main, 12.4, 11.5
Change-Id: I63f9618c596cdad343d4af8393120eabae3fdbd4
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/79708


Tested-by: default avatarOliver Bartsch <bo@cedev.de>
Tested-by: default avatarStefan Bürk <stefan@buerk.tech>
Tested-by: default avatarAnnett Jähnichen <mcmietz@web.de>
Reviewed-by: default avatarOliver Bartsch <bo@cedev.de>
Tested-by: default avatarcore-ci <typo3@b13.com>
Reviewed-by: default avatarStefan Bürk <stefan@buerk.tech>
Tested-by: default avatarAndreas Nedbal <andy@pixelde.su>
Reviewed-by: default avatarAndreas Nedbal <andy@pixelde.su>
parent db1408d9
No related merge requests found
......@@ -122,8 +122,8 @@ class CategoryElement extends HTMLElement{
* It's done once after loading data.
* Later indeterminate state is updated just for the subset of nodes
*/
private loadDataAfter = () => {
this.tree.nodes = this.tree.nodes.map((node: TreeNode) => {
private loadDataAfter = (evt: CustomEvent) => {
this.tree.nodes = evt.detail.nodes.map((node: TreeNode) => {
node.indeterminate = false;
return node;
});
......@@ -144,10 +144,10 @@ class CategoryElement extends HTMLElement{
// foreach ancestor except node itself
let indeterminate = false;
node.parents.forEach((index: number) => {
const node = this.tree.nodes[index];
node.indeterminate = (node.checked || node.indeterminate || indeterminate);
const treeNode = this.tree.nodes[index];
treeNode.indeterminate = (node.checked || node.indeterminate || indeterminate);
// check state for the next level
indeterminate = (node.checked || node.indeterminate || node.checked || node.indeterminate);
indeterminate = (treeNode.checked || treeNode.indeterminate || node.checked || node.indeterminate);
});
}
......
......@@ -112,8 +112,8 @@ export class SelectTreeElement {
* It's done once after loading data.
* Later indeterminate state is updated just for the subset of nodes
*/
private loadDataAfter = () => {
this.tree.nodes = this.tree.nodes.map((node: TreeNode) => {
private loadDataAfter = (evt: CustomEvent) => {
this.tree.nodes = evt.detail.nodes.map((node: TreeNode) => {
node.indeterminate = false;
return node;
});
......@@ -137,10 +137,10 @@ export class SelectTreeElement {
// foreach ancestor except node itself
let indeterminate = false;
node.parents.forEach((index: number) => {
const node = this.tree.nodes[index];
node.indeterminate = (node.checked || node.indeterminate || indeterminate);
const treeNode = this.tree.nodes[index];
treeNode.indeterminate = (node.checked || node.indeterminate || indeterminate);
// check state for the next level
indeterminate = (node.checked || node.indeterminate || node.checked || node.indeterminate);
indeterminate = (treeNode.checked || treeNode.indeterminate || node.checked || node.indeterminate);
});
}
......
......@@ -10,4 +10,4 @@
*
* The TYPO3 project - inspiring people to share!
*/
import"@typo3/backend/form-engine/element/select-tree.js";import"@typo3/backend/form-engine/element/select-tree-toolbar.js";import"@typo3/backend/element/icon-element.js";class CategoryElement extends HTMLElement{constructor(){super(...arguments),this.recordField=null,this.treeWrapper=null,this.tree=null,this.selectNode=e=>{const t=e.detail.node;this.updateAncestorsIndeterminateState(t),this.calculateIndeterminate(this.tree.nodes),this.saveCheckboxes(),this.tree.setup.input.dispatchEvent(new Event("change",{bubbles:!0,cancelable:!0}))},this.loadDataAfter=()=>{this.tree.nodes=this.tree.nodes.map((e=>(e.indeterminate=!1,e))),this.calculateIndeterminate(this.tree.nodes)},this.saveCheckboxes=()=>{this.recordField.value=this.tree.getSelectedNodes().map((e=>e.identifier)).join(",")}}connectedCallback(){this.recordField=this.querySelector("#"+(this.getAttribute("recordFieldId")||"")),this.treeWrapper=this.querySelector("#"+(this.getAttribute("treeWrapperId")||"")),this.recordField&&this.treeWrapper&&(this.tree=document.createElement("typo3-backend-form-selecttree"),this.tree.classList.add("svg-tree-wrapper"),this.tree.setup={id:this.treeWrapper.id,dataUrl:this.generateDataUrl(),readOnlyMode:this.recordField.dataset.readOnly,input:this.recordField,exclusiveNodesIdentifiers:this.recordField.dataset.treeExclusiveKeys,validation:JSON.parse(this.recordField.dataset.formengineValidationRules)[0],expandUpToLevel:this.recordField.dataset.treeExpandUpToLevel,unselectableElements:[]},this.treeWrapper.append(this.tree),this.registerTreeEventListeners())}registerTreeEventListeners(){this.tree.addEventListener("typo3:svg-tree:nodes-prepared",this.loadDataAfter),this.tree.addEventListener("typo3:svg-tree:node-selected",this.selectNode),this.tree.addEventListener("svg-tree:initialized",(()=>{if(this.recordField.dataset.treeShowToolbar){const e=document.createElement("typo3-backend-form-selecttree-toolbar");e.tree=this.tree,this.tree.prepend(e)}})),this.listenForVisibleTree()}listenForVisibleTree(){const e=new IntersectionObserver((t=>{t.forEach((t=>{t.isIntersecting&&(this.tree.dispatchEvent(new Event("svg-tree:visible")),e.unobserve(t.target))}))}));e.observe(this.tree)}generateDataUrl(){return TYPO3.settings.ajaxUrls.record_tree_data+"&"+new URLSearchParams({uid:this.recordField.dataset.uid,command:this.recordField.dataset.command,tableName:this.recordField.dataset.tablename,fieldName:this.recordField.dataset.fieldname,defaultValues:this.recordField.dataset.defaultvalues,overrideValues:this.recordField.dataset.overridevalues,recordTypeValue:this.recordField.dataset.recordtypevalue,flexFormSheetName:this.recordField.dataset.flexformsheetname,flexFormFieldName:this.recordField.dataset.flexformfieldname,flexFormContainerName:this.recordField.dataset.flexformcontainername,dataStructureIdentifier:this.recordField.dataset.datastructureidentifier,flexFormContainerFieldName:this.recordField.dataset.flexformcontainerfieldname,flexFormContainerIdentifier:this.recordField.dataset.flexformcontaineridentifier,flexFormSectionContainerIsNew:this.recordField.dataset.flexformsectioncontainerisnew})}updateAncestorsIndeterminateState(e){let t=!1;e.parents.forEach((e=>{const r=this.tree.nodes[e];r.indeterminate=r.checked||r.indeterminate||t,t=r.checked||r.indeterminate||r.checked||r.indeterminate}))}calculateIndeterminate(e){e.forEach((t=>{(t.checked||t.indeterminate)&&t.parents&&t.parents.length>0&&t.parents.forEach((t=>{e[t].indeterminate=!0}))}))}}window.customElements.define("typo3-formengine-element-category",CategoryElement);
\ No newline at end of file
import"@typo3/backend/form-engine/element/select-tree.js";import"@typo3/backend/form-engine/element/select-tree-toolbar.js";import"@typo3/backend/element/icon-element.js";class CategoryElement extends HTMLElement{constructor(){super(...arguments),this.recordField=null,this.treeWrapper=null,this.tree=null,this.selectNode=e=>{const t=e.detail.node;this.updateAncestorsIndeterminateState(t),this.calculateIndeterminate(this.tree.nodes),this.saveCheckboxes(),this.tree.setup.input.dispatchEvent(new Event("change",{bubbles:!0,cancelable:!0}))},this.loadDataAfter=e=>{this.tree.nodes=e.detail.nodes.map((e=>(e.indeterminate=!1,e))),this.calculateIndeterminate(this.tree.nodes)},this.saveCheckboxes=()=>{this.recordField.value=this.tree.getSelectedNodes().map((e=>e.identifier)).join(",")}}connectedCallback(){this.recordField=this.querySelector("#"+(this.getAttribute("recordFieldId")||"")),this.treeWrapper=this.querySelector("#"+(this.getAttribute("treeWrapperId")||"")),this.recordField&&this.treeWrapper&&(this.tree=document.createElement("typo3-backend-form-selecttree"),this.tree.classList.add("svg-tree-wrapper"),this.tree.setup={id:this.treeWrapper.id,dataUrl:this.generateDataUrl(),readOnlyMode:this.recordField.dataset.readOnly,input:this.recordField,exclusiveNodesIdentifiers:this.recordField.dataset.treeExclusiveKeys,validation:JSON.parse(this.recordField.dataset.formengineValidationRules)[0],expandUpToLevel:this.recordField.dataset.treeExpandUpToLevel,unselectableElements:[]},this.treeWrapper.append(this.tree),this.registerTreeEventListeners())}registerTreeEventListeners(){this.tree.addEventListener("typo3:svg-tree:nodes-prepared",this.loadDataAfter),this.tree.addEventListener("typo3:svg-tree:node-selected",this.selectNode),this.tree.addEventListener("svg-tree:initialized",(()=>{if(this.recordField.dataset.treeShowToolbar){const e=document.createElement("typo3-backend-form-selecttree-toolbar");e.tree=this.tree,this.tree.prepend(e)}})),this.listenForVisibleTree()}listenForVisibleTree(){const e=new IntersectionObserver((t=>{t.forEach((t=>{t.isIntersecting&&(this.tree.dispatchEvent(new Event("svg-tree:visible")),e.unobserve(t.target))}))}));e.observe(this.tree)}generateDataUrl(){return TYPO3.settings.ajaxUrls.record_tree_data+"&"+new URLSearchParams({uid:this.recordField.dataset.uid,command:this.recordField.dataset.command,tableName:this.recordField.dataset.tablename,fieldName:this.recordField.dataset.fieldname,defaultValues:this.recordField.dataset.defaultvalues,overrideValues:this.recordField.dataset.overridevalues,recordTypeValue:this.recordField.dataset.recordtypevalue,flexFormSheetName:this.recordField.dataset.flexformsheetname,flexFormFieldName:this.recordField.dataset.flexformfieldname,flexFormContainerName:this.recordField.dataset.flexformcontainername,dataStructureIdentifier:this.recordField.dataset.datastructureidentifier,flexFormContainerFieldName:this.recordField.dataset.flexformcontainerfieldname,flexFormContainerIdentifier:this.recordField.dataset.flexformcontaineridentifier,flexFormSectionContainerIsNew:this.recordField.dataset.flexformsectioncontainerisnew})}updateAncestorsIndeterminateState(e){let t=!1;e.parents.forEach((r=>{const i=this.tree.nodes[r];i.indeterminate=e.checked||e.indeterminate||t,t=i.checked||i.indeterminate||e.checked||e.indeterminate}))}calculateIndeterminate(e){e.forEach((t=>{(t.checked||t.indeterminate)&&t.parents&&t.parents.length>0&&t.parents.forEach((t=>{e[t].indeterminate=!0}))}))}}window.customElements.define("typo3-formengine-element-category",CategoryElement);
\ No newline at end of file
......@@ -10,4 +10,4 @@
*
* The TYPO3 project - inspiring people to share!
*/
import"@typo3/backend/form-engine/element/select-tree.js";import"@typo3/backend/form-engine/element/select-tree-toolbar.js";import"@typo3/backend/element/icon-element.js";import FormEngine from"@typo3/backend/form-engine.js";export class SelectTreeElement{constructor(e,t,r,i){if(this.recordField=null,this.tree=null,this.selectNode=e=>{const t=e.detail.node;this.updateAncestorsIndeterminateState(t),this.calculateIndeterminate(this.tree.nodes),this.saveCheckboxes(),this.tree.setup.input.dispatchEvent(new Event("change",{bubbles:!0,cancelable:!0}))},this.loadDataAfter=()=>{this.tree.nodes=this.tree.nodes.map((e=>(e.indeterminate=!1,e))),this.calculateIndeterminate(this.tree.nodes)},this.saveCheckboxes=()=>{void 0!==this.recordField&&(this.recordField.value=this.tree.getSelectedNodes().map((e=>e.identifier)).join(","))},r instanceof Function)throw new Error("Function `callback` is not supported anymore since TYPO3 v12.0");this.recordField=document.getElementById(t);const s=document.getElementById(e);this.tree=document.createElement("typo3-backend-form-selecttree"),this.tree.classList.add("svg-tree-wrapper"),this.tree.addEventListener("typo3:svg-tree:nodes-prepared",this.loadDataAfter),this.tree.addEventListener("typo3:svg-tree:node-selected",this.selectNode),i instanceof Array&&this.tree.addEventListener("typo3:svg-tree:node-selected",(()=>{FormEngine.processOnFieldChange(i)}));const a={id:e,dataUrl:this.generateRequestUrl(),readOnlyMode:1===parseInt(this.recordField.dataset.readOnly,10),input:this.recordField,exclusiveNodesIdentifiers:this.recordField.dataset.treeExclusiveKeys,validation:JSON.parse(this.recordField.dataset.formengineValidationRules)[0],expandUpToLevel:this.recordField.dataset.treeExpandUpToLevel,unselectableElements:[]};this.tree.addEventListener("svg-tree:initialized",(()=>{if(this.recordField.dataset.treeShowToolbar){const e=document.createElement("typo3-backend-form-selecttree-toolbar");e.tree=this.tree,this.tree.prepend(e)}})),this.tree.setup=a,s.append(this.tree),this.listenForVisibleTree()}listenForVisibleTree(){const e=new IntersectionObserver((t=>{t.forEach((t=>{t.isIntersecting&&(this.tree.dispatchEvent(new Event("svg-tree:visible")),e.unobserve(t.target))}))}));e.observe(this.tree)}generateRequestUrl(){const e={tableName:this.recordField.dataset.tablename,fieldName:this.recordField.dataset.fieldname,uid:this.recordField.dataset.uid,defaultValues:this.recordField.dataset.defaultvalues,overrideValues:this.recordField.dataset.overridevalues,recordTypeValue:this.recordField.dataset.recordtypevalue,dataStructureIdentifier:this.recordField.dataset.datastructureidentifier,flexFormSheetName:this.recordField.dataset.flexformsheetname,flexFormFieldName:this.recordField.dataset.flexformfieldname,flexFormContainerName:this.recordField.dataset.flexformcontainername,flexFormContainerIdentifier:this.recordField.dataset.flexformcontaineridentifier,flexFormContainerFieldName:this.recordField.dataset.flexformcontainerfieldname,flexFormSectionContainerIsNew:this.recordField.dataset.flexformsectioncontainerisnew,command:this.recordField.dataset.command};return TYPO3.settings.ajaxUrls.record_tree_data+"&"+new URLSearchParams(e)}updateAncestorsIndeterminateState(e){let t=!1;e.parents.forEach((e=>{const r=this.tree.nodes[e];r.indeterminate=r.checked||r.indeterminate||t,t=r.checked||r.indeterminate||r.checked||r.indeterminate}))}calculateIndeterminate(e){e.forEach((t=>{(t.checked||t.indeterminate)&&t.parents&&t.parents.length>0&&t.parents.forEach((t=>{e[t].indeterminate=!0}))}))}}
\ No newline at end of file
import"@typo3/backend/form-engine/element/select-tree.js";import"@typo3/backend/form-engine/element/select-tree-toolbar.js";import"@typo3/backend/element/icon-element.js";import FormEngine from"@typo3/backend/form-engine.js";export class SelectTreeElement{constructor(e,t,r,i){if(this.recordField=null,this.tree=null,this.selectNode=e=>{const t=e.detail.node;this.updateAncestorsIndeterminateState(t),this.calculateIndeterminate(this.tree.nodes),this.saveCheckboxes(),this.tree.setup.input.dispatchEvent(new Event("change",{bubbles:!0,cancelable:!0}))},this.loadDataAfter=e=>{this.tree.nodes=e.detail.nodes.map((e=>(e.indeterminate=!1,e))),this.calculateIndeterminate(this.tree.nodes)},this.saveCheckboxes=()=>{void 0!==this.recordField&&(this.recordField.value=this.tree.getSelectedNodes().map((e=>e.identifier)).join(","))},r instanceof Function)throw new Error("Function `callback` is not supported anymore since TYPO3 v12.0");this.recordField=document.getElementById(t);const a=document.getElementById(e);this.tree=document.createElement("typo3-backend-form-selecttree"),this.tree.classList.add("svg-tree-wrapper"),this.tree.addEventListener("typo3:svg-tree:nodes-prepared",this.loadDataAfter),this.tree.addEventListener("typo3:svg-tree:node-selected",this.selectNode),i instanceof Array&&this.tree.addEventListener("typo3:svg-tree:node-selected",(()=>{FormEngine.processOnFieldChange(i)}));const s={id:e,dataUrl:this.generateRequestUrl(),readOnlyMode:1===parseInt(this.recordField.dataset.readOnly,10),input:this.recordField,exclusiveNodesIdentifiers:this.recordField.dataset.treeExclusiveKeys,validation:JSON.parse(this.recordField.dataset.formengineValidationRules)[0],expandUpToLevel:this.recordField.dataset.treeExpandUpToLevel,unselectableElements:[]};this.tree.addEventListener("svg-tree:initialized",(()=>{if(this.recordField.dataset.treeShowToolbar){const e=document.createElement("typo3-backend-form-selecttree-toolbar");e.tree=this.tree,this.tree.prepend(e)}})),this.tree.setup=s,a.append(this.tree),this.listenForVisibleTree()}listenForVisibleTree(){const e=new IntersectionObserver((t=>{t.forEach((t=>{t.isIntersecting&&(this.tree.dispatchEvent(new Event("svg-tree:visible")),e.unobserve(t.target))}))}));e.observe(this.tree)}generateRequestUrl(){const e={tableName:this.recordField.dataset.tablename,fieldName:this.recordField.dataset.fieldname,uid:this.recordField.dataset.uid,defaultValues:this.recordField.dataset.defaultvalues,overrideValues:this.recordField.dataset.overridevalues,recordTypeValue:this.recordField.dataset.recordtypevalue,dataStructureIdentifier:this.recordField.dataset.datastructureidentifier,flexFormSheetName:this.recordField.dataset.flexformsheetname,flexFormFieldName:this.recordField.dataset.flexformfieldname,flexFormContainerName:this.recordField.dataset.flexformcontainername,flexFormContainerIdentifier:this.recordField.dataset.flexformcontaineridentifier,flexFormContainerFieldName:this.recordField.dataset.flexformcontainerfieldname,flexFormSectionContainerIsNew:this.recordField.dataset.flexformsectioncontainerisnew,command:this.recordField.dataset.command};return TYPO3.settings.ajaxUrls.record_tree_data+"&"+new URLSearchParams(e)}updateAncestorsIndeterminateState(e){let t=!1;e.parents.forEach((r=>{const i=this.tree.nodes[r];i.indeterminate=e.checked||e.indeterminate||t,t=i.checked||i.indeterminate||e.checked||e.indeterminate}))}calculateIndeterminate(e){e.forEach((t=>{(t.checked||t.indeterminate)&&t.parents&&t.parents.length>0&&t.parents.forEach((t=>{e[t].indeterminate=!0}))}))}}
\ No newline at end of file
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment