import { addElt, copyToClipBoard, empty } from 'src/kernel/dom'
import { figRepOrthonorm, figVide } from 'src/kernel/figures'
import './uiCommands.css'
import { getStr } from 'src/kernel/kernel'
const defaultFig = figRepOrthonorm
// les figures par défaut proposées au chargement si on n'en précise pas sont plus loin dans le code
// chercher le commentaire "figures par défaut"
/**
*
* @param {MtgAppLecteurApi} mtgAppLecteurApi
* @param {MtgOptions} mtgOptions
* @param {MtgOptions} mtgOptions.mtgContainer Le conteneur pour la figure
* @param {MtgOptions} mtgOptions.commandsContainer Le conteneur pour la console de commandes
* @param {Array<string|FigDef>} [mtgOptions.commandsFigs] Une liste éventuelle de figures initiales (sinon ce sera figure vide et repère orthonormé)
*/
export async function initDom (mtgAppLecteurApi, mtgOptions) {
/**
* Affiche cette figure
* @param fig
* @returns {Promise}
*/
const setFig = (fig) => {
return mtgAppLecteurApi.setFig({ id, fig })
.then(() => { lastFig = fig })
.catch(error => console.error(error))
}
const { mtgContainer, commandsContainer, commandsFigs } = mtgOptions
// il faut créer un div en absolute pour ace, sinon on voit rien, on lui crée un parent (relative) pour éditeur + bouton
commandsContainer.classList.add('mtgUiCommands')
const divEditorRmq = addElt(commandsContainer, 'div')
divEditorRmq.classList.add('mtgRmq')
const divEditorCt = addElt(commandsContainer, 'div')
divEditorCt.classList.add('mtgUiCommandsEditor')
// et dedans le div qu'on donne à ace
const divEditor = addElt(divEditorCt, 'div')
// et celui qui contiendra les boutons
const divEditorActions = addElt(commandsContainer, 'div')
divEditorActions.classList.add('mtgActions')
const divEditorOutput = addElt(commandsContainer, 'div')
// on passe au conteneur de la figure
let svg = mtgContainer.querySelector('svg')
// on lui colle sa classe
mtgContainer.classList.add('mtgFigCommandedContainer')
// la liste des figures de réinitialisation possibles
const figChoices = []
// on regarde si on nous passe une liste de choix
if (Array.isArray(commandsFigs)) {
// on vérifie ce qu'on nous donne
for (const { name, fig, figRef } of commandsFigs) {
if (!name) {
console.error('Il faut fournir un nom pour les figures listées dans l’option commandsFigs')
continue
}
if (fig) {
figChoices.push({ name, fig })
} else if (figRef) {
// pas un pb de mettre cet import dans une boucle, il n'est réellement fait qu'au 1er appel
const figures = await import('src/kernel/figures.js')
if (figRef in figures) {
figChoices.push({ name, fig: figures[figRef] })
} else {
console.error(`${figRef} n’est pas un code de figure connu, ignoré (${name})`)
}
} else {
console.error(`Il faut fournir fig ou figRef, name seul ne suffit pas (${name})`)
}
}
} else {
// les figures par défaut
figChoices.push({ name: getStr('apiFigEmpty'), fig: figVide })
figChoices.push({ name: getStr('apiRepOrt'), fig: figRepOrthonorm })
}
// on regarde s'il y a déjà une figure dans le dom
let divSvg, figInitiale, lastFig
if (svg) {
if (!svg.parentElement) throw Error('La figure n’est pas insérée comme attendu dans le DOM')
divSvg = svg.parentElement
figInitiale = mtgAppLecteurApi.getBase64Code()
lastFig = figInitiale
// on l'ajoute en premier dans la liste
figChoices.unshift({ name: getStr('apiFigInit'), fig: figInitiale })
} else {
// on initialise avec la première figure de la liste (qui peut être vide,
// pour ne pas laisser la possibilité de changer la figure de départ)
// Si elle est vide et que l'utilisateur n'a pas filé de fig, on prend celle par défaut
lastFig = figChoices[0]?.fig ?? defaultFig
divSvg = addElt(mtgContainer, 'div')
await mtgAppLecteurApi.setFig({ fig: lastFig, container: divSvg, width: 800, height: 600 })
svg = divSvg.querySelector('svg')
// il faut préciser width et height sur le div conteneur car le svg est en absolute, on récupère sa taille
const { width, height } = getComputedStyle(svg)
divSvg.style.width = `${width + 2}px` // pour le border
divSvg.style.height = `${height + 2}px` // pour le border
}
// il faut ajouter un div autour de divSvg car il a un border none imposé,
// pour ses calculs de positionnement
// ce sera divFig, mais après Rmq
const divFigRmq = addElt(mtgContainer, 'div', getStr('apiStartFig'))
divFigRmq.classList.add('mtgRmq')
const divFig = addElt(mtgContainer, 'div')
divFig.classList.add('mtgFigCommanded')
// et on déplace le divSvg dedans
divFig.appendChild(divSvg)
// div pour les boutons mtg
const divFigActions = addElt(mtgContainer, 'div')
divFigActions.classList.add('mtgActions')
// on a besoin de l'id du svg pour certaines méthodes de l'api Lecteur
const { id } = svg
if (figChoices.length) {
addElt(divFigActions, 'span', getStr('apiCodeResetWith'))
if (figChoices.length < 4) {
// jusqu'à 3 on laisse des boutons
for (const { name, fig } of figChoices) {
const resetBtn = addElt(divFigActions, 'button', name)
resetBtn.addEventListener('click', () => {
setFig(fig)
})
}
} else {
// un select avec la liste de choix de figures
const select = addElt(divFigActions, 'select')
let i = 0
for (const { name, fig } of figChoices) {
const option = addElt(select, 'option', name)
option.value = String(i)
if (fig === lastFig) option.selected = true
i++
}
select.addEventListener('input', (event) => {
const index = Number(event.target.value)
const { fig } = figChoices[index]
if (fig) {
setFig(fig)
} else {
console.error(Error(`selectListener with incorrect value ${index}`))
}
})
// avec le select on ne laisse pas le flex par défaut qui fait flotter le texte
// Réinitialiser… loin du select
divFigActions.style.justifyContent = 'center'
}
}
// un div pour afficher la figure ou l'importer, superposé à la figure
const textAreaCt = addElt(mtgContainer, 'div')
textAreaCt.classList.add('mtgFigExchangeCt')
const textAreaRmq = addElt(textAreaCt, 'p')
textAreaRmq.classList.add('mtgRmq')
// attention, il faut que les textes prennent le même nombre de lignes (à peu près),
// sinon on pourrait avoir une 2e barre de scroll vertical
// régler ça dans src/uiCommands/uiCommands.css (.mtgFigExchangeCt textarea height)
const closeRmq = getStr('apiCodeInfoCopy')
const importRmq = getStr('apiCodeInfoPaste')
const textArea = addElt(textAreaCt, 'textarea')
const divPrintActions = addElt(textAreaCt, 'div')
divPrintActions.classList.add('mtgActions')
const closeLabel = 'Fermer'
const importLabel = 'Importer'
// le bouton pour fermer ce "popup"
const closeBtn = addElt(divPrintActions, 'button', closeLabel)
closeBtn.addEventListener('click', () => {
textAreaCt.style.display = 'none'
if (closeBtn.textContent === importLabel) {
const fig = textArea.value.trim()
if (fig) setFig(fig)
// sinon on dit rien, ça correspond à une annulation
}
empty(textArea)
textArea.value = ''
})
// un div pour les boutons de récup
const divFigSave = addElt(mtgContainer, 'div')
divFigSave.classList.add('mtgActions')
// btn import
addElt(divFigSave, 'button', getStr('apiCodeImport'))
.addEventListener('click', () => {
empty(textArea)
textArea.value = ''
closeBtn.textContent = importLabel
textAreaRmq.textContent = importRmq
textAreaCt.style.display = 'block'
})
// btn copier
const copyBtn = addElt(divFigSave, 'button', getStr('apiCodeCopy'))
copyBtn.addEventListener('click', async () => {
const fig = mtgAppLecteurApi.getBase64Code()
if (await copyToClipBoard(fig)) {
textAreaCt.style.display = 'none'
alert(getStr('apiCodeClipboardOk'))
} else {
alert(getStr('apiCodeClipboardKo'))
}
})
// bouton afficher le code de la figure
addElt(divFigSave, 'button', getStr('apiCodeDisplay'))
.addEventListener('click', () => {
empty(textArea)
textArea.value = mtgAppLecteurApi.getBase64Code()
closeBtn.textContent = closeLabel
textAreaRmq.textContent = closeRmq
textAreaCt.style.display = 'block'
})
// bouton télécharger
addElt(divFigSave, 'button', getStr('apiCodeDownload'))
.addEventListener('click', () => {
const doc = mtgAppLecteurApi.getDoc(id)
doc.saveAs('figure')
})
// fcts pour afficher / masquer la figure
// il faut mémoriser les éléments avec l'attribut visibility: visible
// contenu dans divFig car il faut les passer à hidden aussi (sinon ils sont
// visibles même si leur parent est hidden, c'est pas comme display:none)
let visiblesElts, pMsg
const showFig = () => {
if (pMsg?.parentNode) pMsg.parentNode.removeChild(pMsg)
divFig.style.visibility = 'visible'
for (const elt of visiblesElts) elt.setAttribute('visibility', 'visible')
}
const hideFig = ({ delay = 0, message = '' } = {}) => {
return new Promise((resolve) => {
divFig.style.visibility = 'hidden'
visiblesElts = divFig.querySelectorAll('[visibility=visible]')
for (const elt of visiblesElts) elt.setAttribute('visibility', 'hidden')
if (message) {
pMsg = addElt(mtgContainer, 'p', message)
} else if (pMsg?.parentNode) {
pMsg.parentNode.removeChild(pMsg)
}
if (delay) {
setTimeout(() => {
showFig()
resolve()
}, delay)
} else {
resolve()
}
})
}
// et on retourne nos trucs
const resetFig = () => setFig(lastFig)
return { divEditorRmq, divEditor, divEditorActions, divEditorOutput, divFigRmq, divFig, divFigActions, hideFig, resetFig, showFig }
}