Files
ms1inscription-v5/php/inc_fx_ms1_ui.php

362 lines
15 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* MSIN-4512 — UI kit MS1 (chooser / ordre / apply-all / radios / fit).
* Rendu générique : pas de métier PDF ici. Les panneaux passent données + names.
*/
if (!function_exists('fxMs1UiEsc')) {
function fxMs1UiEsc($str) {
return htmlspecialchars((string)$str, ENT_QUOTES, 'UTF-8');
}
/**
* Marqueur kit — à poser sur le root de chaque écran migré.
* Recherche plus tard : data-ms1-ui-kit | MS1-UI-KIT | data-ms1-ui-screen
*
* @param array $tabCfg
* screen (ex. bib-dist-print) — id stable
* patterns (ex. 'P2,P3,P4' ou array)
* ticket (ex. MSIN-4512)
* class (classes CSS extra)
* @return string attributs HTML (sans balise) : class="…" data-…
*/
function fxMs1UiKitRootAttrs($tabCfg = []) {
$strScreen = trim((string)($tabCfg['screen'] ?? ''));
$strTicket = trim((string)($tabCfg['ticket'] ?? ''));
$mixPatterns = $tabCfg['patterns'] ?? '';
if (is_array($mixPatterns)) {
$strPatterns = implode(',', array_map('strval', $mixPatterns));
} else {
$strPatterns = trim((string)$mixPatterns);
}
$strExtraClass = trim((string)($tabCfg['class'] ?? ''));
$strClass = 'ms1-ui-chooser';
if ($strExtraClass !== '') {
$strClass .= ' ' . $strExtraClass;
}
$str = 'class="' . fxMs1UiEsc($strClass) . '" data-ms1-ui-kit="1"';
if ($strScreen !== '') {
$str .= ' data-ms1-ui-screen="' . fxMs1UiEsc($strScreen) . '"';
}
if ($strPatterns !== '') {
$str .= ' data-ms1-ui-patterns="' . fxMs1UiEsc($strPatterns) . '"';
}
if ($strTicket !== '') {
$str .= ' data-ms1-ui-ticket="' . fxMs1UiEsc($strTicket) . '"';
}
return $str;
}
/** Commentaire PHP à coller au-dessus du render (grep MS1-UI-KIT). */
function fxMs1UiKitPhpMarker($strScreen, $strPatterns, $strTicket = '') {
$str = '// MS1-UI-KIT — ' . $strScreen . ' — ' . $strPatterns;
if ($strTicket !== '') {
$str .= ' — ' . $strTicket;
}
return $str;
}
/**
* Bouton « Appliquer à toutes » (P4).
*
* @param array $tabCfg label, class (extra), attrs (string HTML attrs optionnel)
*/
function fxMs1UiRenderApplyAllButton($tabCfg = []) {
$strLabel = (string)($tabCfg['label'] ?? '');
$strExtra = trim((string)($tabCfg['class'] ?? ''));
$strAttrs = (string)($tabCfg['attrs'] ?? '');
$strClass = 'btn btn-outline-secondary btn-sm ms1-ui-chooser-apply-all';
if ($strExtra !== '') {
$strClass .= ' ' . $strExtra;
}
return '<button type="button" class="' . fxMs1UiEsc($strClass) . '"'
. ($strAttrs !== '' ? ' ' . $strAttrs : '')
. '>' . fxMs1UiEsc($strLabel) . '</button>';
}
/**
* Groupe de cases à cocher (P2).
*
* options[] : key, label, width (int optionnel), checked (bool), data (assoc attrs data-*)
*/
function fxMs1UiRenderChooserGroup($tabCfg = []) {
$strLabel = (string)($tabCfg['label'] ?? '');
$strLegendClass = (string)($tabCfg['legend_class'] ?? 'sr-only');
$tabOptions = $tabCfg['options'] ?? [];
if (!is_array($tabOptions)) {
$tabOptions = [];
}
ob_start();
?>
<fieldset class="ms1-ui-chooser-group">
<legend class="<?php echo fxMs1UiEsc($strLegendClass); ?>"><?php echo fxMs1UiEsc($strLabel); ?></legend>
<?php if ($strLabel !== '' && $strLegendClass === 'sr-only') { ?>
<span class="ms1-ui-chooser-group-label small text-muted d-block mb-1"><?php echo fxMs1UiEsc($strLabel); ?></span>
<?php } ?>
<ul class="ms1-ui-chooser-option-list list-unstyled mb-2">
<?php foreach ($tabOptions as $opt) {
$strKey = (string)($opt['key'] ?? '');
if ($strKey === '') {
continue;
}
$strOptLabel = (string)($opt['label'] ?? $strKey);
$intW = (int)($opt['width'] ?? 0);
$blnChecked = !empty($opt['checked']);
$tabData = is_array($opt['data'] ?? null) ? $opt['data'] : [];
?>
<li class="ms1-ui-chooser-option">
<label class="ms1-ui-chooser-option-label">
<input type="checkbox"
class="ms1-ui-chooser-cb"
value="<?php echo fxMs1UiEsc($strKey); ?>"
data-item-key="<?php echo fxMs1UiEsc($strKey); ?>"
data-item-label="<?php echo fxMs1UiEsc($strOptLabel); ?>"
<?php if ($intW > 0) { ?>data-item-width="<?php echo $intW; ?>"<?php } ?>
<?php
foreach ($tabData as $strDk => $strDv) {
echo ' data-' . fxMs1UiEsc(preg_replace('/[^a-z0-9_\-]/i', '', (string)$strDk))
. '="' . fxMs1UiEsc((string)$strDv) . '"';
}
?>
<?php echo $blnChecked ? 'checked' : ''; ?>>
<span><?php echo fxMs1UiEsc($strOptLabel); ?></span>
</label>
</li>
<?php } ?>
</ul>
</fieldset>
<?php
return ob_get_clean();
}
/**
* Liste dordre + ↑↓ (P3).
*
* selected[] : key, label, width ; input_name_tpl avec %s = section_id
*/
function fxMs1UiRenderChooserOrder($tabCfg = []) {
$strLabel = (string)($tabCfg['label'] ?? '');
$strEmpty = (string)($tabCfg['empty_label'] ?? '');
$strLabelUp = (string)($tabCfg['label_up'] ?? '');
$strLabelDown = (string)($tabCfg['label_down'] ?? '');
$strSectionId = (string)($tabCfg['section_id'] ?? '');
$strInputNameTpl = (string)($tabCfg['input_name_tpl'] ?? 'items[%s][]');
$tabSelected = $tabCfg['selected'] ?? [];
if (!is_array($tabSelected)) {
$tabSelected = [];
}
$strInputName = sprintf($strInputNameTpl, $strSectionId);
$blnAny = false;
ob_start();
?>
<div class="ms1-ui-chooser-order-wrap">
<?php if ($strLabel !== '') { ?>
<span class="ms1-ui-chooser-group-label small text-muted d-block mb-1"><?php echo fxMs1UiEsc($strLabel); ?></span>
<?php } ?>
<ol class="ms1-ui-chooser-order list-unstyled mb-0"
data-section-id="<?php echo fxMs1UiEsc($strSectionId); ?>"
data-input-name="<?php echo fxMs1UiEsc($strInputName); ?>">
<?php foreach ($tabSelected as $opt) {
$strKey = (string)($opt['key'] ?? '');
if ($strKey === '') {
continue;
}
$blnAny = true;
$strOptLabel = (string)($opt['label'] ?? $strKey);
$intW = (int)($opt['width'] ?? 0);
echo fxMs1UiRenderChooserOrderItem([
'key' => $strKey,
'label' => $strOptLabel,
'width' => $intW,
'input_name' => $strInputName,
'label_up' => $strLabelUp,
'label_down' => $strLabelDown,
]);
} ?>
</ol>
<p class="ms1-ui-chooser-order-empty text-muted small mb-0"<?php echo $blnAny ? ' hidden' : ''; ?>>
<?php echo fxMs1UiEsc($strEmpty); ?>
</p>
</div>
<?php
return ob_get_clean();
}
/** Une ligne dordre (PHP initial + miroir JS). */
function fxMs1UiRenderChooserOrderItem($tabCfg = []) {
$strKey = (string)($tabCfg['key'] ?? '');
$strLabel = (string)($tabCfg['label'] ?? $strKey);
$intW = (int)($tabCfg['width'] ?? 0);
$strInputName = (string)($tabCfg['input_name'] ?? '');
$strLabelUp = (string)($tabCfg['label_up'] ?? '');
$strLabelDown = (string)($tabCfg['label_down'] ?? '');
ob_start();
?>
<li class="ms1-ui-chooser-order-item"
data-item-key="<?php echo fxMs1UiEsc($strKey); ?>"
<?php if ($intW > 0) { ?>data-item-width="<?php echo $intW; ?>"<?php } ?>>
<span class="ms1-ui-chooser-order-label"><?php echo fxMs1UiEsc($strLabel); ?></span>
<span class="ms1-ui-chooser-order-actions">
<button type="button"
class="btn btn-link btn-sm p-0 ms1-ui-chooser-order-up"
title="<?php echo fxMs1UiEsc($strLabelUp); ?>"
aria-label="<?php echo fxMs1UiEsc($strLabelUp); ?>">
<i class="fa fa-chevron-up" aria-hidden="true"></i>
</button>
<button type="button"
class="btn btn-link btn-sm p-0 ms1-ui-chooser-order-down"
title="<?php echo fxMs1UiEsc($strLabelDown); ?>"
aria-label="<?php echo fxMs1UiEsc($strLabelDown); ?>">
<i class="fa fa-chevron-down" aria-hidden="true"></i>
</button>
</span>
<?php if ($strInputName !== '') { ?>
<input type="hidden" name="<?php echo fxMs1UiEsc($strInputName); ?>" value="<?php echo fxMs1UiEsc($strKey); ?>">
<?php } ?>
</li>
<?php
return ob_get_clean();
}
/**
* Radios exclusifs (P5) — ex. Portrait / Paysage.
*
* options[] : value, label, checked
*/
function fxMs1UiRenderRadios($tabCfg = []) {
$strName = (string)($tabCfg['name'] ?? 'ms1_ui_radio');
$strLegend = (string)($tabCfg['legend'] ?? '');
$strLegendHtml = (string)($tabCfg['legend_html'] ?? ''); // déjà échappé / aide
$strHint = (string)($tabCfg['hint'] ?? '');
$strRadioClass = trim((string)($tabCfg['radio_class'] ?? ''));
$tabOptions = $tabCfg['options'] ?? [];
if (!is_array($tabOptions)) {
$tabOptions = [];
}
ob_start();
?>
<fieldset class="ms1-ui-chooser-radios mb-3">
<legend class="ms1-ui-chooser-radios-legend">
<?php
if ($strLegendHtml !== '') {
echo $strLegendHtml;
} else {
echo fxMs1UiEsc($strLegend);
}
?>
</legend>
<?php foreach ($tabOptions as $opt) {
$strVal = (string)($opt['value'] ?? '');
$strOptLabel = (string)($opt['label'] ?? $strVal);
$blnChecked = !empty($opt['checked']);
$strCbClass = 'ms1-ui-chooser-radio';
if ($strRadioClass !== '') {
$strCbClass .= ' ' . $strRadioClass;
}
?>
<label class="ms1-ui-chooser-radios-opt mr-3">
<input type="radio"
class="<?php echo fxMs1UiEsc($strCbClass); ?>"
name="<?php echo fxMs1UiEsc($strName); ?>"
value="<?php echo fxMs1UiEsc($strVal); ?>"
<?php echo $blnChecked ? 'checked' : ''; ?>>
<span><?php echo fxMs1UiEsc($strOptLabel); ?></span>
</label>
<?php } ?>
<?php if ($strHint !== '') { ?>
<p class="text-muted small mb-1 mt-1"><?php echo fxMs1UiEsc($strHint); ?></p>
<?php } ?>
</fieldset>
<?php
return ob_get_clean();
}
/**
* Feedback contrainte budget (P6) — hint + status + warn.
*/
function fxMs1UiRenderFitFeedback($tabCfg = []) {
$strHint = (string)($tabCfg['hint'] ?? '');
$strHintTpl = (string)($tabCfg['hint_tpl'] ?? '');
$strStatusClass = 'ms1-ui-chooser-fit-status small mb-0';
$strWarnClass = 'ms1-ui-chooser-fit-warn text-danger small mb-0';
ob_start();
?>
<?php if ($strHint !== '' || $strHintTpl !== '') { ?>
<p class="ms1-ui-chooser-fit-hint text-muted small mb-0"
data-hint-tpl="<?php echo fxMs1UiEsc($strHintTpl !== '' ? $strHintTpl : $strHint); ?>">
<?php echo fxMs1UiEsc($strHint); ?>
</p>
<?php } ?>
<p class="<?php echo fxMs1UiEsc($strStatusClass); ?>" aria-live="polite"></p>
<p class="<?php echo fxMs1UiEsc($strWarnClass); ?>" hidden></p>
<?php
return ob_get_clean();
}
/**
* Section chooser complète (titre + apply-all + groupes + ordre).
*
* @param array $tabCfg
* section_id, title, title_suffix_html, apply_all (label|false),
* groups[], order{}, class (extra sur section)
*/
function fxMs1UiRenderChooserSection($tabCfg = []) {
$strSectionId = (string)($tabCfg['section_id'] ?? '');
$strTitle = (string)($tabCfg['title'] ?? '');
$strTitleSuffix = (string)($tabCfg['title_suffix_html'] ?? '');
$strExtraClass = trim((string)($tabCfg['class'] ?? ''));
$tabApply = $tabCfg['apply_all'] ?? null;
$tabGroups = $tabCfg['groups'] ?? [];
$tabOrder = $tabCfg['order'] ?? null;
$strAfterGroupsHtml = (string)($tabCfg['after_groups_html'] ?? '');
$strClass = 'ms1-ui-chooser-section';
if ($strExtraClass !== '') {
$strClass .= ' ' . $strExtraClass;
}
ob_start();
?>
<section class="<?php echo fxMs1UiEsc($strClass); ?>" data-section-id="<?php echo fxMs1UiEsc($strSectionId); ?>">
<div class="ms1-ui-chooser-section-head">
<h3 class="ms1-ui-chooser-section-title">
<?php echo fxMs1UiEsc($strTitle); ?>
<?php echo $strTitleSuffix; ?>
</h3>
<?php
if (is_array($tabApply) && !empty($tabApply['label'])) {
echo fxMs1UiRenderApplyAllButton($tabApply);
}
?>
</div>
<div class="ms1-ui-chooser-section-body">
<?php
if (is_array($tabGroups)) {
foreach ($tabGroups as $tabGroup) {
if (!is_array($tabGroup)) {
continue;
}
echo fxMs1UiRenderChooserGroup($tabGroup);
}
}
echo $strAfterGroupsHtml;
if (is_array($tabOrder)) {
$tabOrder['section_id'] = $strSectionId;
echo fxMs1UiRenderChooserOrder($tabOrder);
}
?>
</div>
</section>
<?php
return ob_get_clean();
}
}