templates/template/template.html.twig line 1
{% extends 'base.html.twig' %}
{% block body %}
<div class="container">
{{ form_start(templateForm,{attr:{"id":"form-template"}}) }}
{% if template.id %}
<input type="hidden" name="id" value="{{ template.id }}">
{% endif %}
<ul class="nav nav-tabs" role="tablist">
<li class="nav-item" role="presentation">
<a class="nav-link active" data-bs-toggle="tab" href="#infos" aria-selected="true" role="tab">Informations</a>
</li>
<li class="nav-item" role="presentation">
<a class="nav-link" data-bs-toggle="tab" href="#template" aria-selected="false" tabindex="-1" role="tab">Template</a>
</li>
{% if template.id %}
<li class="nav-item me-auto" role="presentation">
<a class="nav-link" id="preview" data-bs-toggle="tab" target="_blank" href="{{ path('app_template_view', {id: template.id}) }}" aria-selected="false" tabindex="-1" role="tab">Preview</a>
</li>
{% endif %}
</ul>
<div id="tabTemplateContent" class="tab-content">
<div class="tab-pane fade show active" id="infos" role="tabpanel">
{{ form_row(templateForm.titre) }}
{{ form_row(templateForm.type) }}
{{ include('form/_collection.html.twig',{ formFieldHolder:templateForm.parametres, nom:"parametres-fields-list",id:"addParam", libelle:"Ajouter un paramétre"}) }}
</div>
<div class="tab-pane fade" id="template" role="tabpanel">
<div class="btn-group">
<button class="btn btn-secondary" type="button" data-bs-toggle="offcanvas" data-bs-target="#offcanvasEntities" aria-controls="offcanvasEntities">
Afficher les entités disponible
</button>
<button class="btn btn-sm btn-info" type="button" onclick="addInput()">Input</button>
<button class="btn btn-sm btn-primary" type="button" onclick="addText()">Texte</button>
<div class="btn-group" role="group" aria-label="Button group with nested dropdown" >
<button type="button" class="btn btn-sm btn-primary" onclick="addTitre()">Titre (h1)</button>
<div class="btn-group" role="group">
<button id="btnGroupTitre" type="button" class="btn btn-sm btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button>
<div class="dropdown-menu" aria-labelledby="btnGroupTitre">
<a class="dropdown-item" href="#?" onclick="addTitre(2)">H2</a>
<a class="dropdown-item" href="#?" onclick="addTitre(3)">H3</a>
<a class="dropdown-item" href="#?" onclick="addTitre(4)">H4</a>
<a class="dropdown-item" href="#?" onclick="addTitre(5)">H5</a>
<a class="dropdown-item" href="#?" onclick="addTitre(6)">H6</a>
</div>
</div>
</div>
<button class="btn btn-sm btn-warning" type="button" onclick="addButton()">Bouton</button>
<button class="btn btn-sm btn-outline-primary" type="button" onclick="addBr()">Saut à la ligne</button>
<div class="btn-group" role="group" aria-label="Button group with nested dropdown" >
<button type="button" class="btn btn-sm btn-success" onclick="addTable()">Tableau</button>
<div class="btn-group" role="group">
<button id="btnGroupTitre" type="button" class="btn btn-sm btn-success dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button>
<div class="dropdown-menu" aria-labelledby="btnGroupTitre">
<a class="dropdown-item" href="#?" onclick="addDiv()">Div</a>
</div>
</div>
</div>
</div>
<div class="template-view" ></div>
</div>
</div>
<button class="btn btn-success" type="submit" >Valider</button>
{{ form_end(templateForm) }}
</div>
<div class="off">
<div id="conditions"></div>
<div id="options"></div>
<div id="editors"></div>
<div class="offcanvas offcanvas-start" tabindex="-1" id="offcanvasEntities" aria-labelledby="offcanvasEntitiesLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="offcanvasEntitiesLabel">Entitées disponible</h5>
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<div class="accordion" id="accordionEntities">
{% for entity, fields in entitiesData %}
<div class="accordion-item">
<h2 class="accordion-header" id="headingOne">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse{{ loop.index }}" aria-expanded="false" aria-controls="collapse{{ loop.index }}">
{{ entity }}
</button>
</h2>
<div id="collapse{{ loop.index }}" class="accordion-collapse collapse" aria-labelledby="heading{{ loop.index }}" data-bs-parent="#accordionEntities">
<div class="accordion-body">
<ul>
{% for f in fields %}
<li><a href="#?" onclick="addToTemplate('{{ entity }}','{{ f }}')">{{ f }}</a></li>
{% endfor %}
</ul>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
<div class="card border-primary hide editor" id="text-editor">
<h3 class="card-header">Edition de texte <button onclick="closeCard(this)" type="button" class="btn-close" data-bs-dismiss="text-editor1"></button></h3>
<div class="card-body">
<h6 class="card-subtitle text-muted">Support card subtitle</h6>
</div>
<div class="card-body">
<div id="text-edit"></div>
<button class="btn btn-success valid" >Valider</button>
</div>
</div>
<div class="card border-primary hide editor" id="title-editor">
<h3 class="card-header">Edition de titre <button onclick="closeCard(this)" type="button" class="btn-close" data-bs-dismiss="title-editor"></button></h3>
<div class="card-body">
<div id="title-edit"></div>
<button class="btn btn-success valid" >Valider</button>
</div>
</div>
<div class="card border-info hide editor" id="input-editor">
<h3 class="card-header">Edition de champ <button onclick="closeCard(this)" type="button" class="btn-close" data-bs-dismiss="title-editor"></button></h3>
<div class="card-body">
<div class="form-group">
<label for="nom" class="form-label">Nom du champs</label>
<input type="text" class="form-control dbspace" id="nom">
</div>
<h6>Label</h6>
<div id="label-edit"></div>
<div class="form-group">
<label for="type" class="form-label">Type</label>
<select class="form-select" id="type" onchange="updateTypeOptions(this)">
<option value="text">Text</option>
<option value="textarea">TextArea</option>
<option value="select">Select</option>
<option value="radio">Radio</option>
</select>
</div>
<div class="type-options">
<div class="option text textarea">
<div class="form-group">
<label class="form-label" for="placeholder">Placeholder</label>
<input id="placeholder" type="text" class="form-control" >
</div>
</div>
<div class="option select radio">
<div class="form-group">
<label class="form-label">Choix</label>
<ul class="choix">
<li class="hide" id="template">
<div class="btn-group">
<input type="text" class="form-control" placeholder="Value">
<input type="text" class="form-control" placeholder="Display">
<button type="button" class="btn btn-sm btn-danger" onclick="removeChoix(this)">-</button>
</div>
</li>
</ul>
<button class="btn btn-success" type="button" onclick="addChoix(this)">+</button>
</div>
</div>
</div>
<button class="btn btn-success valid" >Valider</button>
</div>
</div>
<div class="card border-success hide editor" id="table-editor">
<h3 class="card-header">Edition de tableau <button onclick="closeCard(this)" type="button" class="btn-close" data-bs-dismiss="title-editor"></button></h3>
<div class="card-body">
<div class="form-group">
<label for="rows" class="form-label">Lignes</label>
<input type="number" class="form-control" id="rows">
</div>
<div class="form-group">
<label for="cols" class="form-label">Colones</label>
<input type="number" class="form-control" id="cols">
</div>
<div class="form-group">
<label for="couleur" class="form-label">Couleur</label>
<select class="form-select" id="couleur">
<option></option>
{% for col in ["primary", "secondary", "success", "info", "warning", "danger", "dark", "light"] %}
<option value="{{ col }}">{{ col|capitalize }}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label for="border" class="form-label">Bordure</label>
<select class="form-select" id="border">
<option value="Non">Non</option>
<option value="Oui">Oui</option>
</select>
</div>
<div class="form-group">
<label for="striped" class="form-label">Rayé</label>
<select class="form-select" id="striped">
<option value="Non">Non</option>
<option value="Oui">Oui</option>
</select>
</div>
</div>
<div class="card-body preview">
<h6 class="card-subtitle text-muted">Preview</h6>
<table class="table table-bordered table-striped " id="table-view"><tbody></tbody></table>
</div>
<div class="card-body">
<button class="btn btn-success valid" >Valider</button>
</div>
</div>
<div class="card border-warning hide editor" id="button-editor">
<h3 class="card-header">Edition de bouton <button onclick="closeCard(this)" type="button" class="btn-close" data-bs-dismiss="title-editor"></button></h3>
<div class="card-body">
<div id="button-edit"></div>
<div class="form-group">
<label for="size" class="form-label">Taille</label>
<select class="form-select" id="size">
{% for s,t in {"btn":"normal", "btn-sm":"petit", "btn-lg":"grand"} %}
<option value="{{ s }}">{{ t }}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label for="couleur" class="form-label">Couleur</label>
<select class="form-select" id="couleur">
<option></option>
{% for col in ["primary", "secondary", "success", "info", "warning", "danger", "dark", "light", "close"] %}
<option value="{{ col }}">{{ col|capitalize }}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label for="type" class="form-label">Type</label>
<select class="form-select" id="type">
{% for t in ["submit", "reset", "button"] %}
<option value="{{ t }}">{{ t }}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label for="action" class="form-label">Action</label>
<select class="form-select" id="action">
{% for name,acts in actions|default([]) %}
<optgroup label="{{ name }}">
{% for a, t in acts %}
<option value="{{ a }}">{{ t }}</option>
{% endfor %}
</optgroup>
{% endfor %}
</select>
</div>
<button class="btn btn-success valid" >Valider</button>
</div>
</div>
<div class="card border-success hide editor" id="div-editor">
<h3 class="card-header">Edition de div <button onclick="closeCard(this)" type="button" class="btn-close" data-bs-dismiss="title-editor"></button></h3>
<div class="card-body">
<div class="form-group">
<label for="cols" class="form-label">Colones</label>
<input type="number" class="form-control" id="cols">
</div>
<div class="form-group">
<label for="couleur" class="form-label">Couleur</label>
<select class="form-select" id="couleur">
<option></option>
{% for col in ["primary", "secondary", "success", "info", "warning", "danger", "dark", "light"] %}
<option value="{{ col }}">{{ col|capitalize }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="card-body preview">
<h6 class="card-subtitle text-muted">Preview</h6>
<div class="alert row" id="div-view"></div>
</div>
<div class="card-body">
<button class="btn btn-success valid" >Valider</button>
</div>
</div>
<div class="card border-danger hide editor" id="conditions-editor">
<h3 class="card-header">Editions des conditions <button onclick="closeCard(this)" type="button" class="btn-close" data-bs-dismiss="title-editor"></button></h3>
<div class="card-body">
<div class="option">
<div class="form-group">
<table class="choix table">
<thead>
<tr>
<th>Action</th>
<th>Champs</th>
<th colspan="3">Condition</th>
</tr>
</thead>
<tbody>
<tr class="hide" id="template">
<td>
<select class="form-select" id="act" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Tooltip on top">
<option value="show">Afficher</option>
<option value="hide">Cacher</option>
<option value="readOnly">Lecture seule</option>
<option value="disabled">Désactivé</option>
</select>
</td>
<td>
<select class="form-select" id="champs" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Tooltip on top"></select>
<input type="text" class="form-control hide" placeholder="champ entité">
</td>
<td>
<select class="form-select">
<option value="=">Egal</option>
<option value=">">Sup</option>
<option value=">=">Sup ou égal</option>
<option value="<">Inférieur</option>
<option value="<=">Inférieur ou égal</option>
<option value="start">Commence par</option>
<option value="end">Fini par</option>
<option value="cont">Contient</option>
</select>
</td>
<td>
<input type="text" class="form-control" placeholder="condition">
</td>
<td>
<button type="button" class="btn btn-sm btn-danger" onclick="removeCondition(this)">-</button>
</td>
</tr>
</tbody>
</table>
<button class="btn btn-success" type="button" onclick="addCondition(this)">+</button>
</div>
</div>
<button class="btn btn-success valid" >Valider</button>
</div>
</div>
<div class="card border-dark hide editor" id="options-editor">
<h3 class="card-header">Editions des options <button onclick="closeCard(this)" type="button" class="btn-close" data-bs-dismiss="title-editor"></button></h3>
<div class="card-body">
<div class="form-group">
<label for="rows" class="form-label">Défaut</label>
<select class="form-select" id="act">
<option value="show">Afficher</option>
<option value="hide">Cacher</option>
<option value="readOnly">Lecture seule</option>
<option value="disabled">Désactivé</option>
</select> </div>
<div class="form-group">
<label for="rows" class="form-label">Classes</label>
<input type="text" class="form-control" id="classes">
</div>
<div class="form-group">
<label for="css" class="form-label">CSS</label>
<textarea class="form-control" id="css"></textarea>
</div>
<button class="btn btn-success valid" >Valider</button>
</div>
</div>
</div>
{% endblock %}
{% block title %}
Nouvelle template
{% endblock %}
{% block javascripts %}
<script>
let toolbarOptions = [
['bold', 'italic', 'underline', 'strike'], // toggled buttons
['blockquote', 'code-block'],
['link', 'image'],
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
[{ 'script': 'sub'}, { 'script': 'super' }], // superscript/subscript
[{ 'indent': '-1'}, { 'indent': '+1' }], // outdent/indent
[{ 'direction': 'rtl' }], // text direction
[{ 'header': [1, 2, 3, 4, 5, 6, false] }],
[{ 'color': [] }, { 'background': [] }], // dropdown with defaults from theme
[{ 'font': [] }],
[{ 'align': [] }],
['clean'] // remove formatting button
];
let curElement=null;
let curContainer=null;
let dropped = false;
//Editors
function openTextEditor(e) {
let type="text-editor"
let [editor, cid, target] = openEditor(type, e)
if(editor===null)
return
let textEdit =editor.find("#text-edit")
textEdit.attr("id", "text-edit"+cid)
textEdit.html(target.html())
editor.find(".valid").click(validTextEditor)
editor.show()
var quill = addQuillEditor('#text-edit'+cid)
}
function openTitleEditor(e) {
let type="title-editor"
let [editor, cid, target] = openEditor(type, e)
if(editor===null)
return
let textEdit =editor.find("#title-edit")
textEdit.attr("id", "title-edit"+cid)
textEdit.html(target.html())
editor.find(".valid").click(validTitleEditor)
editor.show()
var quill = addQuillEditor('#title-edit'+cid)
}
function openInputEditor(e) {
let type="input-editor"
let [editor, cid, target] = openEditor(type, e)
if(editor===null)
return
let nomEdit =editor.find("#nom")
let nomTarget = target.find("input, select, textarea").attr("name")??""
nomEdit.val(nomTarget.replaceAll("_", " "))
let input= target.find("input, select, textarea")
let inputType = getInputType(input)
let typeEdit =editor.find("#type")
typeEdit.val(inputType)
updateTypeOptions(typeEdit)
let labelEdit =editor.find("#label-edit")
labelEdit.attr("id", "label-edit"+cid)
labelEdit.html(target.find("label").html())
setCustomData(editor, getCustomData(input))
editor.find(".valid").click(validInputEditor)
editor.show()
var quill = addQuillEditor('#label-edit'+cid)
}
function openTableEditor(e) {
let type="table-editor"
let [editor, cid, target] = openEditor(type, e)
if(editor===null)
return
let couleur = target.getClassStartingWith('table-', ["table-bordered", "table-striped"])??"";
editor.find("#couleur").val(couleur.replace("table-", ""))
editor.on("input", updateTablePreview)
let border = target.hasClass("table-bordered")?"Oui":"Non";
editor.find("#border").val(border)
let striped = target.hasClass("table-striped")?"Oui":"Non";
editor.find("#striped").val(striped)
let rows = target.find("tr").length;
editor.find("#rows").val(rows)
let cols = target.find("tr:first td").length;
editor.find("#cols").val(cols)
let tableView =editor.find("#table-view")
$(target.html()).appendTo(tableView)
resetDrag(tableView.find(".updatable"))
editor.find(".valid").click(validTableEditor)
editor.show()
updateTablePreview({target:editor})
}
function openButtonEditor(e) {
let type="button-editor"
let [editor, cid, target] = openEditor(type, e)
if(editor.length===0)
return
let button = $(target.find("button"))
console.log(button, target)
let couleur = button.getClassStartingWith('btn-',["btn-lg", "btn-sm"])??"";
editor.find("#couleur").val(couleur.replace("btn-", ""))
let size = button.hasClass("btn-lg")?"btn-lg":(button.hasClass("btn-sm")?"btn-sm":"btn")
editor.find("#size").val(size)
let typeBt = button.attr("type")??"submit"
editor.find("#type").val(typeBt)
let action = button.attr("action")??"submit"
editor.find("#action").val(action)
let buttonEdit =editor.find("#button-edit")
buttonEdit.attr("id", "button-edit"+cid)
buttonEdit.html(button.html())
editor.find(".valid").click(validButtonEditor)
var quill = addQuillEditor('#button-edit'+cid)
editor.show()
}
function openDivEditor(e) {
let type="div-editor"
let [editor, cid, target] = openEditor(type, e)
if(editor===null)
return
let couleur = target.getClassStartingWith('alert-')??"";
editor.find("#couleur").val(couleur.replace("alert-", ""))
editor.on("input", updateDivPreview)
let cols = target.find(".col").length;
editor.find("#cols").val(cols)
let tableView =editor.find("#div-view")
$(target.html()).appendTo(tableView)
resetDrag(tableView.find(".updatable"))
editor.find(".valid").click(validDivEditor)
editor.show()
updateDivPreview({target:editor})
}
function openConditionsEditor(e) {
let type="conditions-editor"
let [editor, cid, target] = openEditor(type, e)
if(editor===null)
return
let champs = editor.find("#template #champs")
$(".template-view").find("input, textarea, select").each(function () {
let id = $(this).attr("id")
champs.append(`<option value='${id}'>${id}</option>`)
})
champs.append(`<option value='entity'>Entité</option>`)
let conditions = $(".off #conditions")
let div = conditions.find("#"+cid)
if(div.length>0){
div.find(".conds").each(function () {
let values = $(this).text().split("%%")
editor.find(".btn.btn-success").click()
let tr =editor.find("table tr:last")
tr.find("select:eq(0)").val(values[0])
tr.find("select:eq(1)").val(values[1])
tr.find("input:eq(0)").val(values[2])
tr.find("input:eq(0)").toggle( values[1] === "entity")
tr.find("select:eq(2)").val(values[3])
tr.find("input:eq(1)").val(values[4])
})
}
editor.find(".valid").click(validConditionEditor)
editor.show()
}
function openOptionsEditor(e) {
let type="options-editor"
let [editor, cid, target] = openEditor(type, e)
if(editor===null)
return
let options = $(".off #options")
let div = options.find("#"+cid)
if(div.length>0){
let values = div.text().split("%%")
editor.find("select").val(values[0])
editor.find("input").val(values[1])
editor.find("textarea").val(values[2])
}
editor.find(".valid").click(validOptionsEditor)
editor.show()
//var quill = addQuillEditor('#text-edit'+cid)
}
function openEditor(type, e) {
let target = ["conditions-editor", "options-editor"].includes(type)?($(e.target).hasClass("updatable")? $(e.target):$(e.target).parents(".updatable")) :($(e.target).hasClass("has-"+type)? $(e.target):$(e.target).parents(".has-"+type))
let cid= target.attr("cid")
if($("#new-"+type+cid).length>0)
return [null, null, null]
let editor = $("#"+type).clone()
editor.attr("id","new-"+type+cid)
editor.appendTo("#editors")
let count = $(".editor:visible").length
let left =window.innerWidth/2 -editor.outerWidth()/2 + count *20
let top = window.innerHeight/2 - editor.outerHeight()/2+ count *20
editor.css({
left:left,
top:top
})
editor.draggable({
handle: "h3.card-header",
containment : "window",
iframeFix: true,
start:function () {
let modal = $(this)
modal.parent().append(modal)
target.addClass("dragging")
},
drag:function () {
},
stop:function () {
target.removeClass("dragging")
}
});
return [editor, cid, target];
}
function openGoodEditor(event) {
let el = $(this)
let editorType= $(el).getClassStartingWith("has-")??""
switch (editorType){
case "has-text-editor":
openTextEditor(event)
break;
case "has-title-editor":
openTitleEditor(event)
break;
case "has-input-editor":
openInputEditor(event)
break;
case "has-table-editor":
openTableEditor(event)
break;
case "has-div-editor":
openDivEditor(event)
break;
case "has-button-editor":
openButtonEditor(event)
break;
}
}
//Editors option
function addQuillEditor(el) {
var quill = new Quill(el, {
theme: 'snow',
modules:{
toolbar:toolbarOptions
}
});
}
function addCondition(el) {
let div =$(el).parents(".option").find(".choix")
let template =div.find("#template").clone()
template = template.attr("id", "")
template = template.removeClass("hide")
div.append(template)
}
function removeCondition(el) {
$(el).parents("tr").slideUp("normal", function () {
$(el).parents("tr").remove()
})
}
function addChoix(el) {
let div =$(el).parents(".option").find(".choix")
let template =div.find("#template").clone()
template = template.attr("id", "")
template = template.removeClass("hide")
div.append(template)
}
function removeChoix(el) {
$(el).parents("li").slideUp("normal", function () {
$(el).parents("li").remove()
})
}
function updateTypeOptions(el) {
let val = $(el).val()
let options = $(el).parents(".editor").find(".type-options")
options.find(".option").hide()
options.find("."+val).show()
}
function getInputType(el) {
if ($(el).is("input")){
if($(el).is(":text"))return "text";
if($(el).is(":radio"))return "radio";
}
if ($(el).is("textarea") ) return "textarea";
if ($(el).is("select") )return "select";
}
function getCustomData(input) {
let custom ={}
let type = getInputType(input)
switch (type){
case "text":
case "textarea":
custom["placeholder"]=input.attr("placeholder")
break;
case "select":
custom["choix"]={}
input.find("option").each(function () {
custom["choix"][$(this).val()]=$(this).text()
})
break;
}
return custom
}
function setCustomData(editor, custom) {
if(custom["placeholder"]){
editor.find("#placeholder").val(custom["placeholder"])
}
if(custom["choix"]){
let choix = editor.find(".choix").parents(".option")
for (const [key, value] of Object.entries(custom["choix"])) {
if(key==="" && value==="")
continue
addChoix(choix.find(".btn.btn-success"))
let li = choix.find("li:not(#tempalte):last")
li.find("input:eq(0)").val(key)
li.find("input:eq(1)").val(value)
}
}
}
//Save editors
function validTextEditor(e) {
let editor = $(e.target).parents(".editor")
let html =editor.find(".ql-editor").html()
let id = editor.attr("id").replace("new-text-editor", "")
$(".has-text-editor[cid='"+id+"']").html(html)
editor.slideUp("normal", ()=>{
editor.remove()
})
}
function validTitleEditor(e) {
let editor = $(e.target).parents(".editor")
let html =editor.find(".ql-editor").html()
let id = editor.attr("id").replace("new-title-editor", "")
$(".has-title-editor[cid='"+id+"']").html(html)
editor.slideUp("normal", ()=>{
editor.remove()
})
}
function validInputEditor(e) {
let editor = $(e.target).parents(".editor")
let html =editor.find(".ql-editor").html()
let id = editor.attr("id").replace("new-input-editor", "")
let type = editor.find("#type").val()
let nom = editor.find("#nom").val().replaceAll(" ", "_").trim()
let div = $(".has-input-editor[cid='"+id+"']")
let exnom=div.find("input, select, textarea").attr("id")
div.html("")
div.append("<label for='"+nom+"'>"+html+"</label>")
let input =""
switch (type) {
case "text":
case "textarea":
let placeholder = editor.find("#placeholder").val()
input=$(type==="text"?"<input readonly class='form-control'>":"<textarea readonly class='form-control'>")
input.attr("placeholder", placeholder)
break;
case "select":
input = $("<select readonly class='form-select'>")
let choix = editor.find(".choix li:not(#tempalte)")
choix.each(function () {
let v =$(this).find("input:eq(0)").val()
let d =$(this).find("input:eq(1)").val()
$(input).append("<option value='"+v+"'>"+d+"</option>")
})
break;
}
if(exnom!==nom){
$(".off #conditions .conds").each(function () {
let val = $(this).text()
if(val.includes("%%"+exnom+"%%"))
$(this).text( $(this).text().replace("%%"+exnom+"%%", "%%"+nom+"%%"))
})
}
$(input).attr("name", nom)
$(input).attr("id", nom)
div.append(input)
editor.slideUp("normal", ()=>{
editor.remove()
})
}
function validTableEditor(e) {
let editor = $(e.target).parents(".editor")
let id = editor.attr("id").replace("new-table-editor", "")
let couleur = editor.find("#couleur").val()
let border = editor.find("#border").val()==="Oui"
let striped = editor.find("#striped").val()==="Oui"
let div = $(".has-table-editor[cid='"+id+"']")
div.removeClass("table-primary table-secondary table-success table-info table-warning table-danger table-light table-dark")
if(couleur!=="")
div.addClass("table-"+couleur)
div.toggleClass( "table-bordered",border)
div.toggleClass( "table-striped",striped)
let preview = editor.find("#table-view")
div.html(preview.html())
div.find(".updatable").css({
position: "",
left: "",
top: "",
})
editor.slideUp("normal", ()=>{
editor.remove()
})
}
function validButtonEditor(e) {
let editor = $(e.target).parents(".editor")
let id = editor.attr("id").replace("new-button-editor", "")
let couleur = editor.find("#couleur").val()
let type = editor.find("#type").val()
let size = editor.find("#size").val()
let action = editor.find("#action").val()
let div = $(".has-button-editor[cid='"+id+"']")
let button = $(div.find("button"))
button.removeClass()
button.addClass("btn")
button.addClass(size)
button.addClass("btn-"+couleur)
button.attr("type",type)
button.attr("action",action)
let html =editor.find(".ql-editor").html()
button.html(html)
div.find(".updatable").css({
position: "",
left: "",
top: "",
})
editor.slideUp("normal", ()=>{
editor.remove()
})
}
function validDivEditor(e) {
let editor = $(e.target).parents(".editor")
let id = editor.attr("id").replace("new-div-editor", "")
let couleur = editor.find("#couleur").val()
let div = $(".has-div-editor[cid='"+id+"']")
div.removeClass("alert-primary alert-secondary alert-success alert-info alert-warning alert-danger alert-light alert-dark")
if(couleur!=="")
div.addClass("alert-"+couleur)
let preview = editor.find("#div-view")
div.html(preview.html())
div.find(".updatable").css({
position: "",
left: "",
top: "",
})
editor.slideUp("normal", ()=>{
editor.remove()
})
}
function validConditionEditor(e) {
let editor = $(e.target).parents(".editor")
let id = editor.attr("id").replace("new-conditions-editor", "")
let conditions = $(".off #conditions")
let div = $(conditions.find("#"+id)[0]?? "<div id='"+id+"'></div>")
div.html("")
editor.find("table tbody tr:visible").each(function () {
let vals =[]
$(this).find("select, input, textarea").each(function () {
vals.push($(this).val())
})
div.append("<div class='conds'>"+vals.join("%%")+"</div")
})
div.appendTo(conditions)
editor.slideUp("normal", ()=>{
editor.remove()
})
}
function validOptionsEditor(e) {
let editor = $(e.target).parents(".editor")
let id = editor.attr("id").replace("new-options-editor", "")
let options = $(".off #options")
let div = $(options.find("#"+id)[0]?? "<div id='"+id+"'></div>")
div.html("")
let vals=[];
editor.find("select, input, textarea").each(function () {
vals.push($(this).val())
})
div.append("<div class='options'>"+vals.join("%%")+"</div")
div.appendTo(options)
editor.slideUp("normal", ()=>{
editor.remove()
})
}
//Parametres
function addToParams(entity) {
let params = $("#parametres-fields-list")
let inputs = params.find("input")
let [vals , input ]=getEmptyInputs(inputs)
if (!vals.includes(entity)) {
if (input === null) {
$("#addParam").click()
inputs = params.find("input")
input = getEmptyInputs(inputs)[1]
}
input.val(entity)
}
}
function getEmptyInputs(inputs ) {
let vals =[]
let input = null
$(inputs).each(function () {
let val = $(this).val()
if (val === "") {
input = $(this)
}
vals.push(val)
})
return [ vals, input]
}
function addToTemplate(entity, field) {
let value="<{"+entity.toLowerCase()+"."+field+"}>"
let update=$(".template-view .updating")
if(update.length===1){
addToParams(entity.toLowerCase())
addToElement(value, update)
}
$("#offcanvasEntities .offcanvas-header .btn-close").click()
}
//Add Elements
function addInput(bool=false){
let input = "<div class='form-group updatable has-input-editor'><label>Label</label><input readonly class='form-control'></div>"
addElement(input, bool)
}
function addText(bool=false) {
let txt= "<div class='has-text-editor'><p>Texte</p></div>"
addElement(txt, bool)
}
function addBr(bool=false) {
let txt= "<div class='br updatable'><br></div>"
addElement(txt, false)
}
function addTable(bool=false) {
let table= "<table class='updatable table has-table-editor'><tr><td>Case 1</td><td>Case 2</td></tr></table>"
addElement(table, bool)
}
function addDiv(bool=false) {
let div = '<div class="row alert updatable has-div-editor"><div class="col">col</div><div class="col">col</div>'
addElement(div, bool)
}
function addTitre(h=1, bool=false) {
let txt= "<div class='has-title-editor'><h"+h+">Texte</h"+h+"></div"
addElement(txt, bool)
}
function addButton( bool=false) {
let txt= "<div class='has-button-editor'><button>Texte</button></div>"
addElement(txt, bool)
}
function addElement(el, local=false, addId=true) {
let view = local?$(curContainer):$(".template-view")
el = $(el).addClass("updatable")
if (addId)
el.attr("cid", Date.now())
view.append(el)
resetDrag($(el))
}
function addToElement(value, to) {
let txt = $(to).text()
$(to).text(txt+(txt===""?"":" ")+value)
}
//Elements options
function editElement(e) {
let target = $(e.target)
target = target.hasClass("updatable")?target:target.parents('.updatable')
let bool = !target.hasClass("updating")
$(".template-view *").removeClass("updating")
target.toggleClass("updating",bool )
}
function updateTablePreview(e) {
let editor =$(e.target).hasClass("editor")? $(e.target):$(e.target).parents(".editor")
let preview = editor.find("#table-view")
let rows = editor.find("#rows").val()
let cols = editor.find("#cols").val()
let border = editor.find("#border").val()==="Oui"
let striped = editor.find("#striped").val()==="Oui"
let couleur = editor.find("#couleur").val()
preview.removeClass("table-primary table-secondary table-success table-info table-warning table-danger table-light table-dark")
if(couleur!=="")
preview.addClass("table-"+couleur)
preview.toggleClass( "table-bordered",border)
preview.toggleClass( "table-striped",striped)
let resRows= rows-preview.find("tr").length
if(resRows<0){
for (let i = resRows; i <0 ; i++) {
preview.find("tr:last").remove()
}
}
else if (resRows>0) {
preview.append("<tr></tr>".repeat(resRows))
}
let resCols= cols-preview.find("tr:first td").length
if(resCols<0){
for (let i = resCols; i <0 ; i++) {
preview.find("tr").find("td:last").remove()
}
}
preview.find("tr").each(function () {
if(cols > $(this).find("td").length)
$(this).append("<td>Case</td>".repeat(cols - $(this).find("td").length))
})
preview.find("td").droppable({
accept:".updatable",
tolerance: "pointer",
classes: {
"ui-droppable-hover": "ui-state-highlight"
},
over:function (event, ui) {
},
drop: function( event, ui ) {
dropped = $(this);
let id =editor.attr("id")
let matches = id.match(/(\d+)/);
if($(ui.draggable).attr("cid")===matches[0]??""){
$(ui.draggable).appendTo(".template-view")
}
else {
$(ui.draggable).appendTo($(this))
}
$(ui.draggable).css({
position: "",
left: "0",
top: "0",
"z-index": 1,
})
}
})
preview.on("dblclick", ".updatable",openGoodEditor);
preview.on("contextmenu", "tbody>tr>td",openContextAddMenu);
preview.on("contextmenu", ".updatable",openContextMenu);
}
function updateDivPreview(e) {
let editor =$(e.target).hasClass("editor")? $(e.target):$(e.target).parents(".editor")
let preview = editor.find("#div-view")
let cols = editor.find("#cols").val()
let couleur = editor.find("#couleur").val()
preview.removeClass("alert-primary alert-secondary alert-success alert-info alert-warning alert-danger alert-light alert-dark")
if(couleur!=="")
preview.addClass("alert-"+couleur)
let resCols= cols-preview.find(".col").length
if(resCols<0){
for (let i = resCols; i <0 ; i++) {
preview.find(".col:last").remove()
}
}
else if(resCols>0){
preview.append("<div class='col'>Col</div>".repeat(resCols))
}
preview.find("div.col").droppable({
accept:".updatable",
tolerance: "pointer",
greedy: true,
classes: {
"ui-droppable-hover": "ui-state-highlight"
},
over:function (event, ui) {
},
drop: function( event, ui ) {
dropped = $(this);
let id =editor.attr("id")
let matches = id.match(/(\d+)/);
if($(ui.draggable).attr("cid")===matches[0]??""){
$(ui.draggable).appendTo(".template-view")
}
else {
let res = $(ui.draggable).appendTo($(this))
}
$(ui.draggable).css({
position: "",
left: "0",
top: "0",
"z-index": 1,
})
}
})
preview.on("dblclick", ".updatable",openGoodEditor);
preview.on("contextmenu", "div.col",openContextAddMenu);
preview.on("contextmenu", ".updatable",openContextMenu);
}
function resetDrag(el) {
el.draggable({
revert: "invalid",
connectToSortable: ".template-view",
cursorAt: { top: -5, left: -5 },
iframeFix: true,
stop: function() {
$(this).css({
position: "relative",
width:"",
height:""
})
}
});
}
function setSortable(template) {
template.sortable({
placeholder: "ui-state-highlight",
cursor: "move",
stop: function(event, ui) {
if (dropped!==false) {
$(this).sortable("cancel");
ui.item.appendTo($(dropped));
dropped = false;
}
},
/*stop: function(event, ui) {
console.log("stop", event)
if (ui.item.hasClass("has-text-editor"))
$(this).sortable("cancel");
$(this).sortable("enable");
},*/
receive: function(event, ui) {
console.log("receive")
}
})
}
//Context Menu
function openContextMenu(event) {
event.preventDefault();
event.stopPropagation();
curElement=event.currentTarget
contextMenu(event)
}
function openContextAddMenu(event) {
event.preventDefault();
event.stopPropagation();
curContainer=event.currentTarget
contextMenu(event, "custom-context-add-menu")
}
//Context Menu Action
function supprimer() {
if(curElement!=null){
let supp = $(curElement)
let clss = supp.getClassStartingWith("has-")??""
let cid = supp.attr("cid")
let name = clss.replace("has-", "new-")+cid
let editor = $("#"+name)
$(".off #conditions #"+cid).remove()
$(".off #options #"+cid).remove()
editor.fadeOut("normal", ()=>editor.remove())
supp.slideUp("normal", ()=>supp.remove())
}
curElement=null
}
function dupliquer() {
if(curElement!=null){
let el = $(curElement)
let clone = $(el.clone())
let curCid = el.attr("cid")
resetDrag(clone)
clone.insertBefore(el)
let cid =Date.now()
el.attr("cid", cid)
let curConditions =$(".off #conditions #"+curCid)
let cloneConditions =curConditions.clone()
cloneConditions.attr("id", cid)
$(".off #conditions").append(cloneConditions)
let curOptions =$(".off #options #"+curCid)
let cloneOptions =curOptions.clone()
cloneOptions.attr("id", cid)
$(".off #options").append(cloneOptions)
}
curElement=null
}
function conditions(e) {
if(curElement!=null){
openConditionsEditor({
target:curElement,
pageX:(window).event.pageX,
pageY:(window).event.pageY,
})
}
curElement=null
}
function options(e) {
if(curElement!=null){
openOptionsEditor({
target:curElement,
pageX:(window).event.pageX,
pageY:(window).event.pageY,
})
}
curElement=null
}
//Save
function saveTemplate(e) {
e.preventDefault()
let html =$(".template-view").html()
let conditions = $(".off #conditions ").html()
let options = $(".off #options ").html()
let res = {}
res["html"]=html
res["conditions"]=conditions
res["options"]=options
res["conditions_array"]={}
$(".off #conditions>div").each(function(){
let id = $(this).attr("id")
res["conditions_array"][id]=[]
$(this).find(".conds").each(function(){
res["conditions_array"][id].push($(this).html())
})
})
$("#template_data").val(JSON.stringify(res))
$.ajax({
url:"{{ path('app_template_save') }}",
data:$("#form-template").serializeArray(),
method:"POST",
success:function () {
notify("Template", "La Template <i>"+$("#template_titre").val()+"</i> a été enregistré")
}
})
return false
}
$(document).ready(function() {
addMenu( "custom-context-menu", {
"Supprimer":"supprimer()",
"Dupliquer":"dupliquer()",
"Options":"options(this)",
"Conditions":"conditions(this)",
"Option b":"",
},".off")
addMenu( "custom-context-add-menu", {
"Titre":"addTitre(1,true)",
"Texte":"addText(true)",
"Input":"addInput(true)",
"Bouton":"addButton(true)",
"Option a":"alert('OK')",
"Option b":"",
},".off")
let template = $(".template-view")
template.on("contextmenu", ">.updatable",openContextMenu);
template.on("click", ">.updatable",editElement)
template.on("dblclick", ">.updatable",openGoodEditor);
setSortable(template)
let data = $("#template_data").val()
let res = data!==""?JSON.parse(data):[]
if(res["html"])
template.html(res["html"])
if(res["conditions"])
$(".off #conditions").html(res["conditions"])
if(res["options"])
$(".off #options").html(res["options"])
resetDrag(template.find(".updatable"))
$("#form-template").submit(saveTemplate)
$("#preview").off("keydown onkeydown")
$(".off #editors").on("input","#champs" , function () {
let val = $(this).val()
$(this).next("input").toggle( val === "entity")
})
$('[data-bs-toggle="tooltip"]').tooltip()
});
</script>
<script src="//cdn.quilljs.com/1.3.6/quill.min.js"></script>
{% endblock %}
{% block stylesheets %}
<link href="//cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
<style>
.off #conditions, .off #options{
display: none;
}
.off .editor{
width: 40rem;
}
.updatable .updatable, .updatable .updatable:hover{
border: 1px dashed black;
cursor: pointer;
background-color: white;
}
.updatable{
border: 1px dashed rgba(128, 128, 128, 0.7);
cursor: pointer;
background-color: white;
margin: 0;
}
.updatable:hover{
border: 2px dashed blue;
}
.updating, .updating:hover{
border: 1px solid red ;
}
.template-view{
width: 100%;
display: inline-block;
min-height: 20rem ;
border: 1px solid gray;
}
.template-view input, .template-view textarea, .template-view select{
cursor: pointer;
}
#parametres-fields-list>*{
margin: 2px;
}
.type-options .option{
display: none;
}
#table-view .updatable:not(.ui-draggable-dragging){
position: initial !important;
z-index: 1;
}
.template-view .updatable:not(.ui-draggable-dragging){
z-index: 1 !important;
}
.updatable.ui-draggable-dragging{
z-index: 3 !important;
max-width:10rem ;
max-height: 10rem;
overflow: hidden;
}
.dragging{
background-color: red;
}
</style>
{% endblock %}