847 lines
30 KiB
PHP
847 lines
30 KiB
PHP
|
<?php
|
||
|
namespace nur\v\html5;
|
||
|
|
||
|
use nur\A;
|
||
|
use nur\func;
|
||
|
use nur\md;
|
||
|
use nur\v\model\IFormManager;
|
||
|
use nur\v\v;
|
||
|
|
||
|
class Html5FormManager implements IFormManager {
|
||
|
function __construct($options=null, ?array $schema=null) {
|
||
|
$this->resetManager($options, $schema);
|
||
|
}
|
||
|
|
||
|
const MANAGER_OPTIONS_SCHEMA = [
|
||
|
"type" => [null, null, "type de formulaire par défaut"],
|
||
|
];
|
||
|
|
||
|
/** @var array */
|
||
|
protected $stack;
|
||
|
|
||
|
/** @var array */
|
||
|
protected $options;
|
||
|
|
||
|
/** @var array */
|
||
|
protected $schema;
|
||
|
|
||
|
function resetManager($options=null, ?array $schema=null): void {
|
||
|
$this->stack = [];
|
||
|
md::ensure_schema($options, self::MANAGER_OPTIONS_SCHEMA, null, false);
|
||
|
$this->options = $options;
|
||
|
$this->schema = $schema;
|
||
|
$this->inForm = false;
|
||
|
$this->inSection = false;
|
||
|
$this->inGroup = false;
|
||
|
}
|
||
|
|
||
|
function push($options=null, ?array $schema=null): void {
|
||
|
A::push($this->stack, [$this->options, $this->schema]);
|
||
|
md::ensure_schema($options, self::MANAGER_OPTIONS_SCHEMA, null, false);
|
||
|
if ($options !== null) $this->options = $options;
|
||
|
if ($schema !== null) $this->schema = $schema;
|
||
|
}
|
||
|
function pop(): void {
|
||
|
$last = A::pop($this->stack);
|
||
|
if ($last !== null) {
|
||
|
[$this->options, $this->schema] = $last;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected static function build_attrs(?array $attrs, array $options, ?array $set_keys=null, ?array $merge_keys=null): array {
|
||
|
A::update_nx($attrs, $options["attrs"]);
|
||
|
if ($set_keys !== null) {
|
||
|
foreach ($set_keys as $key) {
|
||
|
A::set_nz($attrs, $key, $options[$key]);
|
||
|
}
|
||
|
}
|
||
|
if ($merge_keys !== null) {
|
||
|
foreach ($merge_keys as $key) {
|
||
|
if ($options[$key]) A::merge($attrs[$key], $options[$key]);
|
||
|
A::set_nz($attrs, $key, $options[$key]);
|
||
|
}
|
||
|
}
|
||
|
return $attrs;
|
||
|
}
|
||
|
|
||
|
/** @var bool */
|
||
|
protected $inForm;
|
||
|
/** @var array */
|
||
|
protected $formSuffix;
|
||
|
|
||
|
function started(): bool {
|
||
|
return $this->inForm;
|
||
|
}
|
||
|
|
||
|
const FORM_OPTIONS_SCHEMA = [
|
||
|
"type" => [null, null, "type de formulaire"],
|
||
|
"action" => [null, null, "action du formulaire"],
|
||
|
"method" => [null, null, "méthode du formulaire (post, get)"],
|
||
|
"upload" => [null, null, "ce formulaire est-il utilisé pour uploader des fichiers?"],
|
||
|
"id" => [null, null, "identifiant du formulaire"],
|
||
|
"enctype" => [null, null, "type d'encodage"],
|
||
|
"class" => [null, null, "classes CSS du formulaire"],
|
||
|
"style" => [null, null, "style CSS du formulaire"],
|
||
|
"attrs" => [null, null, "attributs HTML génériques"],
|
||
|
"prefix" => [null, null, "contenu à afficher avant"],
|
||
|
"suffix" => [null, null, "contenu à afficher après"],
|
||
|
];
|
||
|
|
||
|
function start($options=null, ?array $schema=null): array {
|
||
|
$vs = [];
|
||
|
if ($this->inForm) $vs[] = $this->end();
|
||
|
|
||
|
$this->push(null, $schema);
|
||
|
$this->inForm = true;
|
||
|
|
||
|
md::ensure_schema($options, self::FORM_OPTIONS_SCHEMA, null, false);
|
||
|
A::replace_z($options, "type", $this->options["type"]);
|
||
|
|
||
|
$vs[] = q($options["prefix"]);
|
||
|
$this->formSuffix = q($options["suffix"]);
|
||
|
|
||
|
$attrs = self::build_attrs(null, $options, [
|
||
|
"action", "method", "id", "enctype", "style",
|
||
|
], ["class"]);
|
||
|
if ($options["upload"]) {
|
||
|
$attrs["method"] = "post";
|
||
|
$attrs["enctype"] = "multipart/form-data";
|
||
|
}
|
||
|
$vs[] = v::start("form", $attrs);
|
||
|
|
||
|
return $vs;
|
||
|
}
|
||
|
|
||
|
function end(): array {
|
||
|
if (!$this->inForm) return [];
|
||
|
|
||
|
$vs = [];
|
||
|
if ($this->inGroup) $vs[] = $this->endGroup();
|
||
|
if ($this->inSection) $vs[] = $this->endSection();
|
||
|
|
||
|
$vs[] = v::end("form");
|
||
|
$vs[] = $this->formSuffix;
|
||
|
$this->pop();
|
||
|
$this->inForm = false;
|
||
|
$this->formSuffix = null;
|
||
|
|
||
|
return $vs;
|
||
|
}
|
||
|
|
||
|
/** @var bool */
|
||
|
protected $inSection;
|
||
|
/** @var array */
|
||
|
protected $sectionSuffix;
|
||
|
|
||
|
const SECTION_OPTIONS_SCHEMA = [
|
||
|
"section" => [null, null, "libellé de la section"],
|
||
|
"id" => [null, null, "identifiant du champ"],
|
||
|
"class" => [null, null, "classes CSS du champ"],
|
||
|
"style" => [null, null, "style CSS du champ"],
|
||
|
"attrs" => [null, null, "attributs HTML génériques"],
|
||
|
"prefix" => [null, null, "contenu à afficher avant"],
|
||
|
"suffix" => [null, null, "contenu à afficher après"],
|
||
|
];
|
||
|
|
||
|
function section($options=null): array {
|
||
|
if (!$this->inForm) return [];
|
||
|
|
||
|
md::ensure_schema($options, self::SECTION_OPTIONS_SCHEMA, null, false);
|
||
|
|
||
|
$vs = [];
|
||
|
if ($this->inGroup) $vs[] = $this->endGroup();
|
||
|
if ($this->inSection) $vs[] = $this->endSection();
|
||
|
$this->inSection = true;
|
||
|
|
||
|
$vs[] = q($options["prefix"]);
|
||
|
$this->sectionSuffix = q($options["suffix"]);
|
||
|
|
||
|
$attrs = self::build_attrs(null, $options, [
|
||
|
"id", "style",
|
||
|
], ["class"]);
|
||
|
$vs[] = v::tag("h2", [$attrs, q($options["section"])]);
|
||
|
|
||
|
return $vs;
|
||
|
}
|
||
|
function endSection(): array {
|
||
|
if (!$this->inSection) return [];
|
||
|
|
||
|
$vs = [$this->sectionSuffix];
|
||
|
$this->inSection = false;
|
||
|
$this->sectionSuffix = null;
|
||
|
|
||
|
return $vs;
|
||
|
}
|
||
|
|
||
|
/** @var bool */
|
||
|
protected $inGroup;
|
||
|
/** @var array */
|
||
|
protected $groupSuffix;
|
||
|
|
||
|
const GROUP_OPTIONS_SCHEMA = [
|
||
|
"label" => [null, null, "libellé du groupe"],
|
||
|
"prefix" => [null, null, "contenu à afficher avant"],
|
||
|
"suffix" => [null, null, "contenu à afficher après"],
|
||
|
];
|
||
|
|
||
|
function group($options=null): array {
|
||
|
if (!$this->inForm) return [];
|
||
|
|
||
|
md::ensure_schema($options, self::GROUP_OPTIONS_SCHEMA, null, false);
|
||
|
|
||
|
$vs = [];
|
||
|
if ($this->inGroup) $vs[] = $this->endGroup();
|
||
|
$this->inGroup = true;
|
||
|
|
||
|
$vs[] = q($options["prefix"]);
|
||
|
$this->groupSuffix = q($options["suffix"]);
|
||
|
|
||
|
$vs[] = v::start("p", q($options["label"]));
|
||
|
|
||
|
return $vs;
|
||
|
}
|
||
|
function endGroup(): array {
|
||
|
if (!$this->inGroup) return [];
|
||
|
|
||
|
$vs = ["</p>", $this->groupSuffix];
|
||
|
$this->inGroup = false;
|
||
|
$this->groupSuffix = null;
|
||
|
|
||
|
return $vs;
|
||
|
}
|
||
|
|
||
|
const PREFIX = "\n";
|
||
|
|
||
|
/** @var array contenu à afficher avant chaque élément de formulaire */
|
||
|
protected $prefix = [self::PREFIX];
|
||
|
|
||
|
function setPrefix(?string $prefix=null) {
|
||
|
if ($prefix === null) $prefix = static::PREFIX;
|
||
|
$this->prefix = q($prefix);
|
||
|
}
|
||
|
|
||
|
const SUFFIX = null;
|
||
|
|
||
|
/** @var array contenu à afficher après chaque élément de formulaire */
|
||
|
protected $suffix = [self::SUFFIX];
|
||
|
|
||
|
function setSuffix(?string $suffix=null) {
|
||
|
if ($suffix === null) $suffix = static::SUFFIX;
|
||
|
$this->suffix = q($suffix);
|
||
|
}
|
||
|
|
||
|
const FIXED_OPTIONS_SCHEMA = [
|
||
|
"label" => [null, null, "libellé"],
|
||
|
"id" => [null, null, "identifiant"],
|
||
|
"value" => [null, null, "valeur"],
|
||
|
"class" => [null, null, "classes CSS du champ"],
|
||
|
"style" => [null, null, "style CSS du champ"],
|
||
|
"attrs" => [null, null, "attributs HTML génériques"],
|
||
|
"prefix" => [null, null, "contenu à afficher avant"],
|
||
|
"suffix" => [null, null, "contenu à afficher après"],
|
||
|
];
|
||
|
|
||
|
function fixed($label, string $name, $value, ?array $options=null): array {
|
||
|
md::ensure_schema($options, self::FIXED_OPTIONS_SCHEMA, null, false);
|
||
|
A::set_nz($options, "label", $label);
|
||
|
A::set_nz($options, "name", $name);
|
||
|
A::set_nz($options, "value", $value);
|
||
|
A::replace_n_indirect($options, "id", "name");
|
||
|
|
||
|
$label = $options["label"];
|
||
|
$vs = [$this->prefix, q($options["prefix"])];
|
||
|
if ($label) $vs[] = v::start("label", q($label));
|
||
|
|
||
|
$attrs = self::build_attrs(null, $options, [
|
||
|
"id", "style",
|
||
|
], ["class"]);
|
||
|
$vs[] = v::tag("span", [$attrs, q($options["value"])]);
|
||
|
|
||
|
if ($label) $vs[] = v::end("label");
|
||
|
$vs[] = q($options["suffix"]);
|
||
|
$vs[] = $this->suffix;
|
||
|
return $vs;
|
||
|
}
|
||
|
|
||
|
const HIDDEN_OPTIONS_SCHEMA = [
|
||
|
"id" => [null, null, "identifiant du champ"],
|
||
|
"name" => [null, null, "nom du champ"],
|
||
|
"value" => [null, null, "valeur du champ"],
|
||
|
"attrs" => [null, null, "attributs HTML génériques"],
|
||
|
];
|
||
|
|
||
|
function hidden(string $name, $value, ?array $options=null): array {
|
||
|
md::ensure_schema($options, self::HIDDEN_OPTIONS_SCHEMA, null, false);
|
||
|
A::set_nz($options, "name", $name);
|
||
|
A::set_nz($options, "value", $value);
|
||
|
|
||
|
$attrs = self::build_attrs(["type" => "hidden"], $options, [
|
||
|
"id", "name", "value",
|
||
|
]);
|
||
|
return v::tag1("input", $attrs);
|
||
|
}
|
||
|
function hiddens(array $values, string ...$names): array {
|
||
|
if (!$names) $names = array_keys($values);
|
||
|
$vs = [];
|
||
|
foreach ($names as $name) {
|
||
|
$value = A::get($values, $name);
|
||
|
$vs[] = $this->hidden($name, $value);
|
||
|
}
|
||
|
return $vs;
|
||
|
}
|
||
|
|
||
|
const INPUT_OPTIONS_SCHEMA = [
|
||
|
"label" => [null, null, "libellé du champ"],
|
||
|
"id" => [null, null, "identifiant du champ"],
|
||
|
"name" => [null, null, "nom du champ"],
|
||
|
"value" => [null, null, "valeur par défaut du champ"],
|
||
|
"placeholder" => [null, null, "valeur suggérée"],
|
||
|
"required" => [null, null, "ce champ est-il requis?"],
|
||
|
"accesskey" => [null, null, "touche d'accès rapide"],
|
||
|
"tabindex" => [null, null, "index de tabulation"],
|
||
|
"class" => [null, null, "classes CSS du champ"],
|
||
|
"style" => [null, null, "style CSS du champ"],
|
||
|
"attrs" => [null, null, "attributs HTML génériques"],
|
||
|
"prefix" => [null, null, "contenu à afficher avant"],
|
||
|
"suffix" => [null, null, "contenu à afficher après"],
|
||
|
];
|
||
|
|
||
|
protected function _input(string $type, $label, string $name, $value, ?array $options=null): array {
|
||
|
md::ensure_schema($options, self::INPUT_OPTIONS_SCHEMA, null, false);
|
||
|
A::set_nz($options, "label", $label);
|
||
|
A::set_nz($options, "name", $name);
|
||
|
A::set_nz($options, "value", $value);
|
||
|
A::replace_n_indirect($options, "id", "name");
|
||
|
|
||
|
$label = $options["label"];
|
||
|
$vs = [$this->prefix, q($options["prefix"])];
|
||
|
if ($label) $vs[] = v::start("label", q($label));
|
||
|
|
||
|
$attrs = self::build_attrs(["type" => $type], $options, [
|
||
|
"id", "name", "value", "placeholder", "accesskey", "tabindex", "style",
|
||
|
], ["class"]);
|
||
|
if ($options["required"]) $attrs["required"] = "required";
|
||
|
$vs[] = v::tag1("input", $attrs);
|
||
|
|
||
|
if ($label) $vs[] = v::end("label");
|
||
|
$vs[] = q($options["suffix"]);
|
||
|
$vs[] = $this->suffix;
|
||
|
return $vs;
|
||
|
}
|
||
|
|
||
|
function text($label, string $name, $value, ?array $options=null): array {
|
||
|
return $this->_input("text", $label, $name, $value, $options);
|
||
|
}
|
||
|
function texts($label, array $values, string ...$names): array {
|
||
|
if (!$names) $names = array_keys($values);
|
||
|
$vs = [];
|
||
|
if ($label) $vs[] = q($label);
|
||
|
foreach ($names as $name) {
|
||
|
$value = A::get($values, $name);
|
||
|
$vs[] = $this->text(null, $name, $value);
|
||
|
}
|
||
|
return $vs;
|
||
|
}
|
||
|
|
||
|
function password($label, string $name, $value, ?array $options=null): array {
|
||
|
return $this->_input("password", $label, $name, $value, $options);
|
||
|
}
|
||
|
|
||
|
const SELECT_OPTIONS_SCHEMA = [
|
||
|
"label" => [null, null, "libellé du champ"],
|
||
|
"id" => [null, null, "identifiant du champ"],
|
||
|
"name" => [null, null, "nom du champ"],
|
||
|
"value" => [null, null, "valeur par défaut du champ"],
|
||
|
"required" => [null, null, "ce champ est-il requis?"],
|
||
|
"accesskey" => [null, null, "touche d'accès rapide"],
|
||
|
"tabindex" => [null, null, "index de tabulation"],
|
||
|
"items" => [null, null, "liste d'éléments à afficher"],
|
||
|
"items_func" => [null, null, "fonction fournissant une liste d'éléments à afficher"],
|
||
|
"item_value_key" => [null, null, "clé de la valeur d'un élément"],
|
||
|
"item_value_func" => [null, null, "fonction fournissant la clé de la valeur d'un élément"],
|
||
|
"item_text_key" => [null, null, "clé du libellé d'un élément"],
|
||
|
"item_text_func" => [null, null, "fonction fournissant la clé du libellé d'un élément"],
|
||
|
"no_item_value" => [null, null, "valeur si aucun élément n'est sélectionné"],
|
||
|
"no_item_text" => [null, null, "libellé si aucun élément n'est sélectionné"],
|
||
|
"class" => [null, null, "classes CSS du champ"],
|
||
|
"style" => [null, null, "style CSS du champ"],
|
||
|
"attrs" => [null, null, "attributs HTML génériques"],
|
||
|
"prefix" => [null, null, "contenu à afficher avant"],
|
||
|
"suffix" => [null, null, "contenu à afficher après"],
|
||
|
"option_prefix" => [null, null, "contenu à afficher avant chaque option"],
|
||
|
"option_suffix" => [null, null, "contenu à afficher après chaque option"],
|
||
|
];
|
||
|
|
||
|
private function vof(
|
||
|
array $options,
|
||
|
?string $value_key=null,
|
||
|
$value_func=null,
|
||
|
$item=null, ?string $item_key=null, ?int $item_index = null,
|
||
|
...$item_func_args) {
|
||
|
if ($value_key !== null) {
|
||
|
$value = A::get($options, $value_key);
|
||
|
if ($value !== null) return $value;
|
||
|
}
|
||
|
if ($value_func !== null) {
|
||
|
$func = A::get($options, $value_func);
|
||
|
if ($func !== null) {
|
||
|
func::ensure_func($func, $this, $item_func_args);
|
||
|
$value = func::call($func, ...$item_func_args);
|
||
|
if ($value !== null) return $value;
|
||
|
}
|
||
|
}
|
||
|
if (A::is_array($item)) {
|
||
|
$array_item = A::with($item);
|
||
|
if ($item_key !== null) {
|
||
|
$value_key = A::get($options, $item_key);
|
||
|
if ($value_key !== null) {
|
||
|
$value = A::get($array_item, $value_key);
|
||
|
if ($value !== null) return $value;
|
||
|
}
|
||
|
}
|
||
|
if ($item_index !== null) {
|
||
|
$index = 0;
|
||
|
foreach ($array_item as $value) {
|
||
|
if ($index === $item_index) {
|
||
|
return $value;
|
||
|
}
|
||
|
$index++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return $item;
|
||
|
}
|
||
|
|
||
|
function select($label, string $name, $value, ?array $options=null): array {
|
||
|
md::ensure_schema($options, self::SELECT_OPTIONS_SCHEMA, null, false);
|
||
|
A::set_nz($options, "label", $label);
|
||
|
A::set_nz($options, "name", $name);
|
||
|
A::set_nz($options, "value", $value);
|
||
|
A::replace_n_indirect($options, "id", "name");
|
||
|
|
||
|
$value = $options["value"];
|
||
|
$items = $this->vof($options, "items", "items_func");
|
||
|
$no_item_value = $options["no_item_value"];
|
||
|
$no_item_text = $options["no_item_text"];
|
||
|
|
||
|
$oos = array();
|
||
|
if ($no_item_value !== null) {
|
||
|
$oos[] = array(
|
||
|
"value" => $no_item_value,
|
||
|
"text" => $no_item_text,
|
||
|
"selected" => $value == $no_item_value,
|
||
|
);
|
||
|
}
|
||
|
foreach ($items as $key => $item) {
|
||
|
$item_value = $this->vof($options, null, "item_value_func", $item, "item_value_key", 0, $item, $key);
|
||
|
$item_text = $this->vof($options, null, "item_text_func", $item, "item_text_key", 1, $item, $key);
|
||
|
if (!$item_text) $item_text = $item_value;
|
||
|
$oos[] = array(
|
||
|
"value" => $item_value,
|
||
|
"text" => $item_text,
|
||
|
"selected" => $value == $item_value,
|
||
|
);
|
||
|
}
|
||
|
|
||
|
$label = $options["label"];
|
||
|
$vs = [$this->prefix, q($options["prefix"])];
|
||
|
if ($label) $vs[] = v::start("label", q($label));
|
||
|
|
||
|
$attrs = self::build_attrs(null, $options, [
|
||
|
"id", "name", "value", "accesskey", "tabindex", "style",
|
||
|
], ["class"]);
|
||
|
if ($options["required"]) $attrs["required"] = "required";
|
||
|
$vs[] = v::start("select", $attrs);
|
||
|
foreach ($oos as $oo) {
|
||
|
$vs[] = q($options["option_prefix"]);
|
||
|
$vs[] = v::tag("option", [
|
||
|
"value" => $oo["value"],
|
||
|
"selected" => $oo["selected"]? "selected": false,
|
||
|
q($oo["text"]),
|
||
|
]);
|
||
|
$vs[] = q($options["option_suffix"]);
|
||
|
}
|
||
|
$vs[] = v::end("select");
|
||
|
|
||
|
if ($label) $vs[] = v::end("label");
|
||
|
$vs[] = q($options["suffix"]);
|
||
|
$vs[] = $this->suffix;
|
||
|
return $vs;
|
||
|
}
|
||
|
|
||
|
const CHECKBOX_OPTIONS_SCHEMA = [
|
||
|
"text" => [null, null, "libellé de la case à cocher"],
|
||
|
"id" => [null, null, "identifiant de la case à cocher"],
|
||
|
"name" => [null, null, "nom du champ"],
|
||
|
"value" => [null, null, "valeur du champ"],
|
||
|
"checked" => [null, null, "la case est-elle cochée?"],
|
||
|
"accesskey" => [null, null, "touche d'accès rapide"],
|
||
|
"tabindex" => [null, null, "index de tabulation"],
|
||
|
"class" => [null, null, "classes CSS du champ"],
|
||
|
"style" => [null, null, "style CSS du champ"],
|
||
|
"attrs" => [null, null, "attributs HTML génériques"],
|
||
|
"prefix" => [null, null, "contenu à afficher avant"],
|
||
|
"suffix" => [null, null, "contenu à afficher après"],
|
||
|
];
|
||
|
|
||
|
function checkbox($text, string $name, $value, ?bool $checked=null, ?array $options=null): array {
|
||
|
md::ensure_schema($options, self::CHECKBOX_OPTIONS_SCHEMA, null, false);
|
||
|
A::set_nz($options, "text", $text);
|
||
|
A::set_nz($options, "name", $name);
|
||
|
A::set_nz($options, "value", $value);
|
||
|
A::set_nz($options, "checked", $checked);
|
||
|
A::replace_n_indirect($options, "id", "name");
|
||
|
|
||
|
$text = $options["text"];
|
||
|
if (is_string($text)) $text = " $text";
|
||
|
$vs = [q($options["prefix"])];
|
||
|
if ($text) $vs[] = v::start("label");
|
||
|
|
||
|
$attrs = self::build_attrs(["type" => "checkbox"], $options, [
|
||
|
"id", "name", "value", "accesskey", "tabindex", "style",
|
||
|
], ["class"]);
|
||
|
if ($options["checked"]) $attrs["checked"] = "checked";
|
||
|
$vs[] = v::tag1("input", $attrs);
|
||
|
|
||
|
if ($text) {
|
||
|
$vs[] = q($text);
|
||
|
$vs[] = v::end("label");
|
||
|
}
|
||
|
$vs[] = q($options["suffix"]);
|
||
|
return $vs;
|
||
|
}
|
||
|
|
||
|
const CHECKBOXES_OPTIONS_SCHEMA = [
|
||
|
"label" => [null, null, "libellé du champ"],
|
||
|
"id" => [null, null, "identifiant du champ"],
|
||
|
"name" => [null, null, "nom du champ"],
|
||
|
"values" => [null, null, "valeurs cochées par défaut"],
|
||
|
"accesskey" => [null, null, "touche d'accès rapide"],
|
||
|
"tabindex" => [null, null, "index de tabulation"],
|
||
|
"items" => [null, null, "liste d'éléments à afficher"],
|
||
|
"items_func" => [null, null, "fonction fournissant une liste d'éléments à afficher"],
|
||
|
"item_value_key" => [null, null, "clé de la valeur d'un élément"],
|
||
|
"item_value_func" => [null, null, "fonction fournissant la clé de la valeur d'un élément"],
|
||
|
"item_text_key" => [null, null, "clé du libellé d'un élément"],
|
||
|
"item_text_func" => [null, null, "fonction fournissant la clé du libellé d'un élément"],
|
||
|
"class" => [null, null, "classes CSS du champ"],
|
||
|
"style" => [null, null, "style CSS du champ"],
|
||
|
"attrs" => [null, null, "attributs HTML génériques"],
|
||
|
"prefix" => [null, null, "contenu à afficher avant"],
|
||
|
"suffix" => [null, null, "contenu à afficher après"],
|
||
|
"checkbox_prefix" => [null, null, "contenu à afficher avant chaque case à cocher"],
|
||
|
"checkbox_suffix" => [null, null, "contenu à afficher après chaque case à cocher"],
|
||
|
];
|
||
|
|
||
|
function checkboxes($label, string $name, $values, ?array $options=null): array {
|
||
|
md::ensure_schema($options, self::CHECKBOXES_OPTIONS_SCHEMA, null, false);
|
||
|
A::set_nz($options, "label", $label);
|
||
|
A::set_nz($options, "name", $name);
|
||
|
A::set_nz($options, "values", $values);
|
||
|
|
||
|
$name = $options["name"];
|
||
|
$values = A::with($options["values"]);
|
||
|
$items = $this->vof($options, "items", "items_func");
|
||
|
|
||
|
$oos = array();
|
||
|
foreach ($items as $key => $item) {
|
||
|
$item_value = $this->vof($options, null, "item_value_func", $item, "item_value_key", 0, $item, $key);
|
||
|
$item_text = $this->vof($options, null, "item_text_func", $item, "item_text_key", 1, $item, $key);
|
||
|
if (!$item_text) $item_text = $item_value;
|
||
|
$oos[] = array(
|
||
|
"name" => "${name}[$key]",
|
||
|
"value" => $item_value,
|
||
|
"text" => $item_text,
|
||
|
"checked" => in_array($item_value, $values),
|
||
|
);
|
||
|
}
|
||
|
|
||
|
$label = $options["label"];
|
||
|
$vs = [$this->prefix, q($options["prefix"])];
|
||
|
if ($label) $vs[] = v::start("label", q($label));
|
||
|
|
||
|
$attrs = self::build_attrs(null, $options, [
|
||
|
"id", "accesskey", "tabindex", "style",
|
||
|
], ["class"]);
|
||
|
$first = true;
|
||
|
foreach ($oos as $oo) {
|
||
|
$vs[] = q($options["checkbox_prefix"]);
|
||
|
$vs[] = $this->checkbox($oo["text"],
|
||
|
$oo["name"], $oo["value"], $oo["checked"],
|
||
|
["attrs" => $attrs]);
|
||
|
$vs[] = q($options["checkbox_suffix"]);
|
||
|
if ($first && $label) $vs[] = v::end("label");
|
||
|
$attrs["id"] = false; # id uniquement sur le premier
|
||
|
unset($attrs["accesskey"]); # accesskey uniquement sur le premier
|
||
|
unset($attrs["tabindex"]); # accesskey uniquement sur le premier
|
||
|
$first = false;
|
||
|
}
|
||
|
|
||
|
if ($first && $label) $vs[] = v::end("label");
|
||
|
$vs[] = q($options["suffix"]);
|
||
|
$vs[] = $this->suffix;
|
||
|
return $vs;
|
||
|
}
|
||
|
|
||
|
const RADIOBUTTON_OPTIONS_SCHEMA = [
|
||
|
"text" => [null, null, "libellé du bouton radio"],
|
||
|
"id" => [null, null, "identifiant du bouton radio"],
|
||
|
"name" => [null, null, "nom du champ"],
|
||
|
"value" => [null, null, "valeur du champ"],
|
||
|
"checked" => [null, null, "le bouton est-il sélectionné?"],
|
||
|
"accesskey" => [null, null, "touche d'accès rapide"],
|
||
|
"tabindex" => [null, null, "index de tabulation"],
|
||
|
"class" => [null, null, "classes CSS du champ"],
|
||
|
"style" => [null, null, "style CSS du champ"],
|
||
|
"attrs" => [null, null, "attributs HTML génériques"],
|
||
|
"prefix" => [null, null, "contenu à afficher avant"],
|
||
|
"suffix" => [null, null, "contenu à afficher après"],
|
||
|
];
|
||
|
|
||
|
function radiobutton($text, string $name, $value, ?bool $checked=null, ?array $options=null): array {
|
||
|
md::ensure_schema($options, self::RADIOBUTTON_OPTIONS_SCHEMA, null, false);
|
||
|
A::set_nz($options, "text", $text);
|
||
|
A::set_nz($options, "name", $name);
|
||
|
A::set_nz($options, "value", $value);
|
||
|
A::set_nz($options, "checked", $checked);
|
||
|
A::replace_n_indirect($options, "id", "name");
|
||
|
|
||
|
$text = $options["text"];
|
||
|
if (is_string($text)) $text = " $text";
|
||
|
$vs = [q($options["prefix"])];
|
||
|
if ($text) $vs[] = v::start("label");
|
||
|
|
||
|
$attrs = self::build_attrs(["type" => "radio"], $options, [
|
||
|
"id", "name", "value", "accesskey", "tabindex", "style",
|
||
|
], ["class"]);
|
||
|
if ($options["checked"]) $attrs["checked"] = "checked";
|
||
|
$vs[] = v::tag1("input", $attrs);
|
||
|
|
||
|
if ($text) {
|
||
|
$vs[] = q($text);
|
||
|
$vs[] = v::end("label");
|
||
|
}
|
||
|
$vs[] = q($options["suffix"]);
|
||
|
return $vs;
|
||
|
}
|
||
|
|
||
|
const RADIOBUTTONS_OPTIONS_SCHEMA = [
|
||
|
"label" => [null, null, "libellé du champ"],
|
||
|
"id" => [null, null, "identifiant du champ"],
|
||
|
"name" => [null, null, "nom du champ"],
|
||
|
"value" => [null, null, "valeur cochée par défaut"],
|
||
|
"accesskey" => [null, null, "touche d'accès rapide"],
|
||
|
"tabindex" => [null, null, "index de tabulation"],
|
||
|
"items" => [null, null, "liste d'éléments à afficher"],
|
||
|
"items_func" => [null, null, "fonction fournissant une liste d'éléments à afficher"],
|
||
|
"item_value_key" => [null, null, "clé de la valeur d'un élément"],
|
||
|
"item_value_func" => [null, null, "fonction fournissant la clé de la valeur d'un élément"],
|
||
|
"item_text_key" => [null, null, "clé du libellé d'un élément"],
|
||
|
"item_text_func" => [null, null, "fonction fournissant la clé du libellé d'un élément"],
|
||
|
"class" => [null, null, "classes CSS du champ"],
|
||
|
"style" => [null, null, "style CSS du champ"],
|
||
|
"attrs" => [null, null, "attributs HTML génériques"],
|
||
|
"prefix" => [null, null, "contenu à afficher avant"],
|
||
|
"suffix" => [null, null, "contenu à afficher après"],
|
||
|
"radiobutton_prefix" => [null, null, "contenu à afficher avant chaque bouton radio"],
|
||
|
"radiobutton_suffix" => [null, null, "contenu à afficher après chaque bouton radio"],
|
||
|
];
|
||
|
|
||
|
function radiobuttons($label, string $name, $value, ?array $options): array {
|
||
|
md::ensure_schema($options, self::RADIOBUTTONS_OPTIONS_SCHEMA, null, false);
|
||
|
A::set_nz($options, "label", $label);
|
||
|
A::set_nz($options, "name", $name);
|
||
|
A::set_nz($options, "value", $value);
|
||
|
A::replace_n_indirect($options, "id", "name");
|
||
|
|
||
|
$id = $options["id"];
|
||
|
$name = $options["name"];
|
||
|
$value = $options["value"];
|
||
|
$items = $this->vof($options, "items", "items_func");
|
||
|
|
||
|
$oos = array();
|
||
|
$i = 0;
|
||
|
foreach ($items as $key => $item) {
|
||
|
$item_value = $this->vof($options, null, "item_value_func", $item, "item_value_key", 0, $item, $key);
|
||
|
$item_text = $this->vof($options, null, "item_text_func", $item, "item_text_key", 1, $item, $key);
|
||
|
if (!$item_text) $item_text = $item_value;
|
||
|
$oos[] = array(
|
||
|
"id" => "$id$i",
|
||
|
"name" => "$name",
|
||
|
"value" => $item_value,
|
||
|
"text" => $item_text,
|
||
|
"checked" => $value === $item_value,
|
||
|
);
|
||
|
$i++;
|
||
|
}
|
||
|
|
||
|
$label = $options["label"];
|
||
|
$vs = [$this->prefix, q($options["prefix"])];
|
||
|
if ($label) $vs[] = v::start("label", q($label));
|
||
|
|
||
|
$attrs = self::build_attrs(null, $options, [
|
||
|
"accesskey", "tabindex", "style",
|
||
|
], ["class"]);
|
||
|
$first = true;
|
||
|
foreach ($oos as $oo) {
|
||
|
$vs[] = q($options["radiobutton_prefix"]);
|
||
|
$vs[] = $this->radiobutton($oo["text"],
|
||
|
$oo["name"], $oo["value"], $oo["checked"],
|
||
|
["id" => $oo["id"], "attrs" => $attrs]);
|
||
|
$vs[] = q($options["radiobutton_suffix"]);
|
||
|
if ($first && $label) $vs[] = v::end("label");
|
||
|
unset($attrs["accesskey"]); # accesskey uniquement sur le premier
|
||
|
unset($attrs["tabindex"]); # accesskey uniquement sur le premier
|
||
|
$first = false;
|
||
|
}
|
||
|
|
||
|
if ($first && $label) $vs[] = v::end("label");
|
||
|
$vs[] = q($options["suffix"]);
|
||
|
$vs[] = $this->suffix;
|
||
|
return $vs;
|
||
|
}
|
||
|
|
||
|
const TEXTAREA_OPTIONS_SCHEMA = [
|
||
|
"label" => [null, null, "libellé du champ"],
|
||
|
"id" => [null, null, "identifiant du champ"],
|
||
|
"name" => [null, null, "nom du champ"],
|
||
|
"value" => [null, null, "valeur par défaut du champ"],
|
||
|
"placeholder" => [null, null, "valeur suggérée"],
|
||
|
"required" => [null, null, "ce champ est-il requis?"],
|
||
|
"accesskey" => [null, null, "touche d'accès rapide"],
|
||
|
"tabindex" => [null, null, "index de tabulation"],
|
||
|
"class" => [null, null, "classes CSS du champ"],
|
||
|
"style" => [null, null, "style CSS du champ"],
|
||
|
"attrs" => [null, null, "attributs HTML génériques"],
|
||
|
"prefix" => [null, null, "contenu à afficher avant"],
|
||
|
"suffix" => [null, null, "contenu à afficher après"],
|
||
|
];
|
||
|
|
||
|
function textarea($label, string $name, $value, ?array $options=null): array {
|
||
|
md::ensure_schema($options, self::TEXTAREA_OPTIONS_SCHEMA, null, false);
|
||
|
A::set_nz($options, "label", $label);
|
||
|
A::set_nz($options, "name", $name);
|
||
|
A::set_nz($options, "value", $value);
|
||
|
A::replace_n_indirect($options, "id", "name");
|
||
|
|
||
|
$label = $options["label"];
|
||
|
$vs = [$this->prefix, q($options["prefix"])];
|
||
|
if ($label) $vs[] = v::start("label", q($label));
|
||
|
|
||
|
$attrs = self::build_attrs(null, $options, [
|
||
|
"id", "name", "placeholder", "accesskey", "tabindex", "style",
|
||
|
], ["class"]);
|
||
|
if ($options["required"]) $attrs["required"] = "required";
|
||
|
$vs[] = v::tag("textarea", [$attrs, q($options["value"])]);
|
||
|
|
||
|
if ($label) $vs[] = v::end("label");
|
||
|
$vs[] = q($options["suffix"]);
|
||
|
$vs[] = $this->suffix;
|
||
|
return $vs;
|
||
|
}
|
||
|
|
||
|
const FILE_OPTIONS_SCHEMA = [
|
||
|
"label" => [null, null, "libellé du champ"],
|
||
|
"id" => [null, null, "identifiant du champ"],
|
||
|
"name" => [null, null, "nom du champ"],
|
||
|
"accept" => [null, ".pdf,image/*", "types MIME acceptés"],
|
||
|
"required" => [null, null, "ce champ est-il requis?"],
|
||
|
"accesskey" => [null, null, "touche d'accès rapide"],
|
||
|
"tabindex" => [null, null, "index de tabulation"],
|
||
|
"class" => [null, null, "classes CSS du champ"],
|
||
|
"style" => [null, null, "style CSS du champ"],
|
||
|
"attrs" => [null, null, "attributs HTML génériques"],
|
||
|
"prefix" => [null, null, "contenu à afficher avant"],
|
||
|
"suffix" => [null, null, "contenu à afficher après"],
|
||
|
];
|
||
|
|
||
|
function file($label, string $name, ?array $options=null): array {
|
||
|
md::ensure_schema($options, self::FILE_OPTIONS_SCHEMA, null, false);
|
||
|
A::set_nz($options, "label", $label);
|
||
|
A::set_nz($options, "name", $name);
|
||
|
A::replace_n_indirect($options, "id", "name");
|
||
|
|
||
|
$label = $options["label"];
|
||
|
$vs = [$this->prefix, q($options["prefix"])];
|
||
|
if ($label) $vs[] = v::start("label", q($label));
|
||
|
|
||
|
$attrs = self::build_attrs(["type" => "file"], $options, [
|
||
|
"id", "name", "accept", "accesskey", "tabindex", "style",
|
||
|
], ["class"]);
|
||
|
if ($options["required"]) $attrs["required"] = "required";
|
||
|
$vs[] = v::tag1("input", $attrs);
|
||
|
|
||
|
if ($label) $vs[] = v::end("label");
|
||
|
$vs[] = q($options["suffix"]);
|
||
|
$vs[] = $this->suffix;
|
||
|
return $vs;
|
||
|
}
|
||
|
|
||
|
const SUBMIT_OPTIONS_SCHEMA = [
|
||
|
"submit" => [null, null, "libellé du bouton de soumission"],
|
||
|
"id" => [null, null, "identifiant du bouton"],
|
||
|
"name" => [null, null, "nom du champ"],
|
||
|
"value" => [null, null, "valeur du champ"],
|
||
|
"formmethod" => [null, null, "méthode de soumission (post, get)"],
|
||
|
"formaction" => [null, null, "action pour la soumission"],
|
||
|
"formenctype" => [null, null, "type d'encodage pour la soumission"],
|
||
|
"accesskey" => [null, null, "touche d'accès rapide"],
|
||
|
"tabindex" => [null, null, "index de tabulation"],
|
||
|
"type" => [null, null, "type de bouton (submit, reset)"],
|
||
|
"class" => [null, null, "classes CSS du bouton"],
|
||
|
"style" => [null, null, "style CSS du bouton"],
|
||
|
"attrs" => [null, null, "attributs HTML génériques"],
|
||
|
"prefix" => [null, null, "contenu à afficher avant"],
|
||
|
"suffix" => [null, null, "contenu à afficher après"],
|
||
|
];
|
||
|
|
||
|
function submit($options=null): array {
|
||
|
md::ensure_schema($options, self::SUBMIT_OPTIONS_SCHEMA, null, false);
|
||
|
A::replace_z($options, "type", "submit");
|
||
|
|
||
|
$vs = [$this->prefix, q($options["prefix"])];
|
||
|
|
||
|
$attrs = self::build_attrs(null, $options, [
|
||
|
"type", "id", "name", "value", "formaction", "formmethod", "formenctype",
|
||
|
"accesskey", "tabindex", "style",
|
||
|
], ["class"]);
|
||
|
$vs[] = v::tag("button", [$attrs, q($options["submit"])]);
|
||
|
|
||
|
$vs[] = q($options["suffix"]);
|
||
|
$vs[] = $this->suffix;
|
||
|
return $vs;
|
||
|
}
|
||
|
|
||
|
const RESET_OPTIONS_SCHEMA = [
|
||
|
"reset" => [null, null, "libellé du bouton de soumission"],
|
||
|
"id" => [null, null, "identifiant du bouton"],
|
||
|
"name" => [null, null, "nom du champ"],
|
||
|
"value" => [null, null, "valeur du champ"],
|
||
|
"accesskey" => [null, null, "touche d'accès rapide"],
|
||
|
"tabindex" => [null, null, "index de tabulation"],
|
||
|
"type" => [null, null, "type de bouton (submit, reset)"],
|
||
|
"class" => [null, null, "classes CSS du bouton"],
|
||
|
"style" => [null, null, "style CSS du bouton"],
|
||
|
"attrs" => [null, null, "attributs HTML génériques"],
|
||
|
"prefix" => [null, null, "contenu à afficher avant"],
|
||
|
"suffix" => [null, null, "contenu à afficher après"],
|
||
|
];
|
||
|
|
||
|
function reset($options=null): array {
|
||
|
md::ensure_schema($options, self::RESET_OPTIONS_SCHEMA, null, false);
|
||
|
A::replace_z($options, "type", "reset");
|
||
|
|
||
|
$vs = [$this->prefix, q($options["prefix"])];
|
||
|
|
||
|
$attrs = self::build_attrs(null, $options, [
|
||
|
"type", "id", "name", "value",
|
||
|
"accesskey", "tabindex", "style",
|
||
|
], ["class"]);
|
||
|
$vs[] = v::tag("button", [$attrs, q($options["reset"])]);
|
||
|
|
||
|
$vs[] = q($options["suffix"]);
|
||
|
$vs[] = $this->suffix;
|
||
|
return $vs;
|
||
|
}
|
||
|
}
|