Files

42 lines
16 KiB
JavaScript

/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
import B from"@typo3/core/document-service.js";import{SeverityEnum as q}from"@typo3/backend/enum/severity.js";import"bootstrap";import A from"@typo3/backend/modal.js";import G from"@typo3/core/security-utility.js";import{property as w,customElement as z}from"lit/decorators.js";import{LitElement as F,html as l,nothing as p}from"lit";import{classMap as O}from"lit/directives/class-map.js";import{styleMap as P}from"lit/directives/style-map.js";import{createRef as U,ref as L}from"lit/directives/ref.js";import{CodeMirrorElement as j}from"@typo3/backend/code-editor/element/code-mirror-element.js";import o from"~labels/core.wizards";import I from"~labels/core.common";import W from"~labels/backend.alt_doc";var f=function(d,e,t,i){var r=arguments.length,n=r<3?e:i===null?i=Object.getOwnPropertyDescriptor(e,t):i,s;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")n=Reflect.decorate(d,e,t,i);else for(var a=d.length-1;a>=0;a--)(s=d[a])&&(n=(r<3?s(n):r>3?s(e,t,n):s(e,t))||n);return r>3&&n&&Object.defineProperty(e,t,n),n},c,m;(function(d){d.none="",d.slide="slide",d.collect="collect",d.collectReverse="collectReverse"})(m||(m={}));let u=c=class extends F{constructor(){super(...arguments),this.colCount=1,this.rowCount=1,this.readOnly=!1,this.fieldName="",this.data=[],this.codeMirrorConfig={},this.previewAreaRef=U(),this.codeMirrorRef=U(),this.defaultCell={spanned:0,rowspan:1,colspan:1,name:"",colpos:"",column:void 0,identifier:"",slideMode:m.none},this.modalButtonClickHandler=e=>{const t=e.target,i=e.currentTarget;t.name==="cancel"?i.hideModal():t.name==="ok"&&(this.setName(i.querySelector(".t3js-grideditor-field-name").value,i.userData.col,i.userData.row),this.setColumn(parseInt(i.querySelector(".t3js-grideditor-field-colpos").value,10),i.userData.col,i.userData.row),this.setIdentifier(i.querySelector(".t3js-grideditor-field-identifier").value,i.userData.col,i.userData.row),this.setSlideMode(i.querySelector(".t3js-grideditor-field-slide-mode").value,i.userData.col,i.userData.row),this.requestUpdate(),this.writeConfig(this.export2LayoutRecord()),i.hideModal())},this.addColumnHandler=e=>{e.preventDefault(),this.addColumn(),this.requestUpdate(),this.writeConfig(this.export2LayoutRecord())},this.removeColumnHandler=e=>{e.preventDefault(),this.removeColumn(),this.requestUpdate(),this.writeConfig(this.export2LayoutRecord())},this.addRowTopHandler=e=>{e.preventDefault(),this.addRowTop(),this.requestUpdate(),this.writeConfig(this.export2LayoutRecord())},this.addRowBottomHandler=e=>{e.preventDefault(),this.addRowBottom(),this.requestUpdate(),this.writeConfig(this.export2LayoutRecord())},this.removeRowTopHandler=e=>{e.preventDefault(),this.removeRowTop(),this.requestUpdate(),this.writeConfig(this.export2LayoutRecord())},this.removeRowBottomHandler=e=>{e.preventDefault(),this.removeRowBottom(),this.requestUpdate(),this.writeConfig(this.export2LayoutRecord())},this.linkEditorHandler=e=>{e.preventDefault();const t=e.currentTarget;this.showOptions(Number(t.dataset.col),Number(t.dataset.row))},this.linkExpandRightHandler=e=>{e.preventDefault();const t=e.currentTarget;this.addColspan(Number(t.dataset.col),Number(t.dataset.row)),this.requestUpdate(),this.writeConfig(this.export2LayoutRecord())},this.linkShrinkLeftHandler=e=>{e.preventDefault();const t=e.currentTarget;this.removeColspan(Number(t.dataset.col),Number(t.dataset.row)),this.requestUpdate(),this.writeConfig(this.export2LayoutRecord())},this.linkExpandDownHandler=e=>{e.preventDefault();const t=e.currentTarget;this.addRowspan(Number(t.dataset.col),Number(t.dataset.row)),this.requestUpdate(),this.writeConfig(this.export2LayoutRecord())},this.linkShrinkUpHandler=e=>{e.preventDefault();const t=e.currentTarget;this.removeRowspan(Number(t.dataset.col),Number(t.dataset.row)),this.requestUpdate(),this.writeConfig(this.export2LayoutRecord())}}static stripMarkup(e){return new G().stripHtml(e)}async connectedCallback(){await B.ready(),this.field=document.querySelector('input[name="'+this.fieldName+'"]'),this.addVisibilityObserver(this),super.connectedCallback()}firstUpdated(){this.writeConfig(this.export2LayoutRecord())}createRenderRoot(){return this}render(){return l`<div class=${O({grideditor:!0,"grideditor-readonly":this.readOnly})}>${this.readOnly?p:this.renderControls("top",!1)}<div class=grideditor-editor><div class=t3js-grideditor>${this.renderEditorGrid()}</div></div>${this.readOnly?p:this.renderControls("right",!0)} ${this.readOnly?p:this.renderControls("bottom",!1)}<div class=grideditor-preview>${this.renderPreview()}</div></div>`}renderControls(e,t){const i={top:this.addRowTopHandler,right:this.addColumnHandler,bottom:this.addRowBottomHandler},r={top:o.get("grid_addRow"),right:o.get("grid_addColumn"),bottom:o.get("grid_addRow")},n={top:this.removeRowTopHandler,right:this.removeColumnHandler,bottom:this.removeRowBottomHandler},s={top:o.get("grid_removeRow"),right:o.get("grid_removeColumn"),bottom:o.get("grid_removeRow")};return l`<div class="grideditor-control grideditor-control-${e}"><div class=${O({"btn-group":!t,"btn-group-vertical":t})}><button @click=${i[e]} class="btn btn-default btn-sm" title=${r[e]}><typo3-backend-icon identifier=actions-plus size=small></typo3-backend-icon></button> <button @click=${n[e]} class="btn btn-default btn-sm" title=${s[e]}><typo3-backend-icon identifier=actions-minus size=small></typo3-backend-icon></button></div></div>`}renderEditorGrid(){const e=[];for(let t=0;t<this.rowCount;t++)if(this.data[t].length!==0)for(let r=0;r<this.colCount;r++){const n=this.data[t][r];n.spanned!==1&&e.push(this.renderGridCell(t,r,n))}return l`<div class=grideditor-editor-grid>${e}</div>`}renderGridCell(e,t,i){const r={"--grideditor-cell-col":t+1,"--grideditor-cell-colspan":i.colspan,"--grideditor-cell-row":e+1,"--grideditor-cell-rowspan":i.rowspan};return l`<div class=grideditor-cell style=${P(r)}><div class=grideditor-cell-actions>${this.readOnly?p:l`<button @click=${this.linkEditorHandler} class="t3js-grideditor-link-editor grideditor-action grideditor-action-edit" data-row=${e} data-col=${t} title=${o.get("grid_editCell")}><typo3-backend-icon identifier=actions-open size=small></typo3-backend-icon></button> ${this.cellCanSpanRight(t,e)?l`<button @click=${this.linkExpandRightHandler} class="t3js-grideditor-link-expand-right grideditor-action grideditor-action-expand-right" data-row=${e} data-col=${t} title=${o.get("grid_cell_merge_right")}><typo3-backend-icon identifier=actions-caret-right size=small></typo3-backend-icon></button>`:p} ${this.cellCanShrinkLeft(t,e)?l`<button @click=${this.linkShrinkLeftHandler} class="t3js-grideditor-link-shrink-left grideditor-action grideditor-action-shrink-left" data-row=${e} data-col=${t} title=${o.get("grid_cell_split_horizontal")}><typo3-backend-icon identifier=actions-caret-left size=small></typo3-backend-icon></button>`:p} ${this.cellCanSpanDown(t,e)?l`<button @click=${this.linkExpandDownHandler} class="t3js-grideditor-link-expand-down grideditor-action grideditor-action-expand-down" data-row=${e} data-col=${t} title=${o.get("grid_cell_merge_down")}><typo3-backend-icon identifier=actions-caret-down size=small></typo3-backend-icon></button>`:p} ${this.cellCanShrinkUp(t,e)?l`<button @click=${this.linkShrinkUpHandler} class="t3js-grideditor-link-shrink-up grideditor-action grideditor-action-shrink-up" data-row=${e} data-col=${t} title=${o.get("grid_cell_split_vertical")}><typo3-backend-icon identifier=actions-caret-up size=small></typo3-backend-icon></button>`:p}`}</div><div class=grideditor-cell-info><strong>${o.get("grid_name")}:</strong> ${i.name?c.stripMarkup(i.name):o.get("grid_notSet")}<br><strong>${o.get("grid_column")}:</strong> ${typeof i.column>"u"||isNaN(i.column)?o.get("grid_notSet"):i.column} ${i.identifier?.length?l`<br><strong>${o.get("grid_identifier")}:</strong> ${i.identifier}`:""} ${(i.slideMode?.toString()||"")!==""?l`<br><strong>${o.get("grid_slideMode")}:</strong> ${i.slideMode.toString()}`:""}</div></div>`}renderPreview(){return Object.keys(this.codeMirrorConfig).length===0?l`<label>${W.get("buttons.pageTsConfig")}</label><div class="t3js-grideditor-preview-config grideditor-preview"><textarea class="t3js-tsconfig-preview-area form-control" rows=25 readonly ${L(this.previewAreaRef)}></textarea></div>`:l`<typo3-t3editor-codemirror class="t3js-grideditor-preview-config grideditor-preview" label=${this.codeMirrorConfig.label} panel=${this.codeMirrorConfig.panel} mode=${this.codeMirrorConfig.mode} nolazyload=true readonly ${L(this.codeMirrorRef)}><textarea class="t3js-tsconfig-preview-area form-control" ${L(this.previewAreaRef)}></textarea></typo3-t3editor-codemirror>`}getNewCell(){return structuredClone(this.defaultCell)}writeConfig(e){this.field.value=e;const t=e.split(`
`);let i="";for(const a of t)a&&(i+=" "+a+`
`);const r=`mod.web_layout.BackendLayouts {
exampleKey {
title = Example
icon = content-container-columns-2
config {
`+i.replace(new RegExp("\\t","g")," ")+` }
}
}
`,n=this.previewAreaRef.value;n instanceof HTMLTextAreaElement&&(n.value=r);const s=this.codeMirrorRef.value;s instanceof j&&s.setContent(r)}addRowTop(){const e=[];for(let t=0;t<this.colCount;t++){const i=this.getNewCell();i.name=t+"x"+this.data.length,e[t]=i}this.data.unshift(e),this.rowCount++}addRowBottom(){const e=[];for(let t=0;t<this.colCount;t++){const i=this.getNewCell();i.name=t+"x"+this.data.length,e[t]=i}this.data.push(e),this.rowCount++}removeRowTop(){if(this.rowCount<=1)return!1;const e=[];for(let t=1;t<this.rowCount;t++)e.push(this.data[t]);for(let t=0;t<this.colCount;t++)this.data[0][t].spanned===1&&this.findUpperCellWidthRowspanAndDecreaseByOne(t,0);return this.data=e,this.rowCount--,!0}removeRowBottom(){if(this.rowCount<=1)return!1;const e=[];for(let t=0;t<this.rowCount-1;t++)e.push(this.data[t]);for(let t=0;t<this.colCount;t++)this.data[this.rowCount-1][t].spanned===1&&this.findUpperCellWidthRowspanAndDecreaseByOne(t,this.rowCount-1);return this.data=e,this.rowCount--,!0}findUpperCellWidthRowspanAndDecreaseByOne(e,t){const i=this.getCell(e,t-1);return i?(i.spanned===1?this.findUpperCellWidthRowspanAndDecreaseByOne(e,t-1):i.rowspan>1&&this.removeRowspan(e,t-1),!0):!1}removeColumn(){if(this.colCount<=1)return!1;const e=[];for(let t=0;t<this.rowCount;t++){const i=[];for(let r=0;r<this.colCount-1;r++)i.push(this.data[t][r]);this.data[t][this.colCount-1].spanned===1&&this.findLeftCellWidthColspanAndDecreaseByOne(this.colCount-1,t),e.push(i)}return this.data=e,this.colCount--,!0}findLeftCellWidthColspanAndDecreaseByOne(e,t){const i=this.getCell(e-1,t);return i?(i.spanned===1?this.findLeftCellWidthColspanAndDecreaseByOne(e-1,t):i.colspan>1&&this.removeColspan(e-1,t),!0):!1}addColumn(){for(let e=0;e<this.rowCount;e++){const t=this.getNewCell();t.name=this.colCount+"x"+e,this.data[e].push(t)}this.colCount++}setName(e,t,i){const r=this.getCell(t,i);return r?(r.name=c.stripMarkup(e),!0):!1}setColumn(e,t,i){const r=this.getCell(t,i);return r?(r.column=parseInt(e.toString(),10),!0):!1}setIdentifier(e,t,i){const r=this.getCell(t,i);return r?(r.identifier=c.stripMarkup(e),!0):!1}setSlideMode(e,t,i){const r=this.getCell(t,i);return r?(r.slideMode=m[e],!0):!1}showOptions(e,t){const i=this.getCell(e,t);if(!i)return!1;let r;i.column===0?r=0:i.column?r=parseInt(i.column.toString(),10):r="";const n=document.createElement("div"),s=document.createElement("div");s.classList.add("form-group");const a=document.createElement("label");a.classList.add("form-label");const b=document.createElement("input");b.classList.add("form-control");const S=s.cloneNode(!0),k=a.cloneNode(!0);k.innerText=o.get("grid_nameHelp"),k.htmlFor="grideditor-field-name";const h=b.cloneNode(!0);h.id="grideditor-field-name",h.type="text",h.classList.add("t3js-grideditor-field-name"),h.name="name",h.value=c.stripMarkup(i.name)||"",S.append(k,h);const N=s.cloneNode(!0),R=a.cloneNode(!0);R.innerText=o.get("grid_columnHelp"),R.htmlFor="grideditor-field-colpos";const g=b.cloneNode(!0);g.type="text",g.classList.add("t3js-grideditor-field-colpos"),g.id="grideditor-field-colpos",g.name="column",g.value=r.toString(),N.append(R,g);const _=s.cloneNode(!0),x=a.cloneNode(!0);x.innerText=o.get("grid_identifierHelp"),x.htmlFor="grideditor-field-identifier";const v=b.cloneNode(!0);h.type="text",v.classList.add("t3js-grideditor-field-identifier"),v.id="grideditor-field-identifier",v.name="identifier",v.value=typeof i.identifier=="string"?c.stripMarkup(i.identifier):"",_.append(x,v);const H=s.cloneNode(!0),$=a.cloneNode(!0);$.innerText=o.get("grid_slideModeHelp"),$.htmlFor="grideditor-field-slide-mode";const C=document.createElement("select");C.classList.add("form-select","t3js-grideditor-field-slide-mode"),C.id="grideditor-field-slide-mode",C.name="slideMode",C.value=c.stripMarkup(i.slideMode?.toString())||"",Object.keys(m).map(D=>{const T=D!=="none"?D:"",E=m[D],y=document.createElement("option");y.value=E,y.text=T,y.selected=E===i.slideMode?.toString(),C.appendChild(y)}),H.append($,C),n.append(S,N,_,H);const M=A.show(o.get("grid_windowTitle"),n,q.notice,[{active:!0,btnClass:"btn-default",name:"cancel",text:I.get("cancel")},{btnClass:"btn-primary",name:"ok",text:I.get("ok")}]);return M.userData.col=e,M.userData.row=t,M.addEventListener("button.clicked",this.modalButtonClickHandler),!0}getCell(e,t){return e>this.colCount-1||t>this.rowCount-1?!1:this.data.length>t-1&&this.data[t].length>e-1?this.data[t][e]:null}cellCanSpanRight(e,t){if(e===this.colCount-1)return!1;const i=this.getCell(e,t);if(!i)return!1;let r;if(i.rowspan>1){for(let n=t;n<t+i.rowspan;n++)if(r=this.getCell(e+i.colspan,n),!r||r.spanned===1||r.colspan>1||r.rowspan>1)return!1}else if(r=this.getCell(e+i.colspan,t),!r||i.spanned===1||r.spanned===1||r.colspan>1||r.rowspan>1)return!1;return!0}cellCanSpanDown(e,t){if(t===this.rowCount-1)return!1;const i=this.getCell(e,t);if(!i)return!1;let r;if(i.colspan>1){for(let n=e;n<e+i.colspan;n++)if(r=this.getCell(n,t+i.rowspan),!r||r.spanned===1||r.colspan>1||r.rowspan>1)return!1}else if(r=this.getCell(e,t+i.rowspan),!r||i.spanned===1||r.spanned===1||r.colspan>1||r.rowspan>1)return!1;return!0}cellCanShrinkLeft(e,t){return this.data[t][e].colspan>1}cellCanShrinkUp(e,t){return this.data[t][e].rowspan>1}addColspan(e,t){const i=this.getCell(e,t);if(!i||!this.cellCanSpanRight(e,t))return!1;for(let r=t;r<t+i.rowspan;r++)this.data[r][e+i.colspan].spanned=1;return i.colspan+=1,!0}addRowspan(e,t){const i=this.getCell(e,t);if(!i||!this.cellCanSpanDown(e,t))return!1;for(let r=e;r<e+i.colspan;r++)this.data[t+i.rowspan][r].spanned=1;return i.rowspan+=1,!0}removeColspan(e,t){const i=this.getCell(e,t);if(!i||!this.cellCanShrinkLeft(e,t))return!1;i.colspan-=1;for(let r=t;r<t+i.rowspan;r++)this.data[r][e+i.colspan].spanned=0;return!0}removeRowspan(e,t){const i=this.getCell(e,t);if(!i||!this.cellCanShrinkUp(e,t))return!1;i.rowspan-=1;for(let r=e;r<e+i.colspan;r++)this.data[t+i.rowspan][r].spanned=0;return!0}export2LayoutRecord(){let e=`backend_layout {
colCount = `+this.colCount+`
rowCount = `+this.rowCount+`
rows {
`;for(let t=0;t<this.rowCount;t++){e+=" "+(t+1)+` {
`,e+=` columns {
`;let i=0;for(let r=0;r<this.colCount;r++){const n=this.getCell(r,t);if(n&&!n.spanned){const s=c.stripMarkup(n.name)||"";i++,e+=" "+i+` {
`,e+=" name = "+(s||r+"x"+t)+`
`,n.colspan>1&&(e+=" colspan = "+n.colspan+`
`),n.rowspan>1&&(e+=" rowspan = "+n.rowspan+`
`),typeof n.column=="number"&&(e+=" colPos = "+n.column+`
`),typeof n.identifier=="string"&&n.identifier.length&&(e+=" identifier = "+n.identifier+`
`),n.slideMode!==void 0&&n.slideMode!==m.none&&(e+=" slideMode = "+n.slideMode.toString()+`
`),e+=` }
`}}e+=` }
`,e+=` }
`}return e+=` }
}
`,e}addVisibilityObserver(e){e.offsetParent===null&&new IntersectionObserver(t=>{t.forEach(i=>{const r=this.codeMirrorRef.value;i.intersectionRatio>0&&r instanceof j&&r.requestUpdate()})}).observe(e)}};f([w({type:Number})],u.prototype,"colCount",void 0),f([w({type:Number})],u.prototype,"rowCount",void 0),f([w({type:Boolean})],u.prototype,"readOnly",void 0),f([w({type:String})],u.prototype,"fieldName",void 0),f([w({type:Array})],u.prototype,"data",void 0),f([w({type:Object})],u.prototype,"codeMirrorConfig",void 0),u=c=f([z("typo3-backend-grid-editor")],u);export{u as GridEditor};