implémentation contenu web
This commit is contained in:
parent
d17b826b5a
commit
b274372c41
@ -261,7 +261,7 @@ class v {
|
|||||||
return $pieces;
|
return $pieces;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function _list(?iterable $values, ?string $sep=", ", ?string $prefix=null, ?string $suffix=null, ?callable $function): array {
|
private static function _list(?iterable $values, ?string $sep=", ", ?string $prefix=null, ?string $suffix=null, ?callable $function=null): array {
|
||||||
$vs = [];
|
$vs = [];
|
||||||
if ($values !== null) {
|
if ($values !== null) {
|
||||||
$functx = $function !== null? func::_prepare($function): null;
|
$functx = $function !== null? func::_prepare($function): null;
|
||||||
@ -285,7 +285,7 @@ class v {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static final function simple_list(?iterable $values, ?string $sep=", ", ?string $prefix=null, ?string $suffix=null): array {
|
static final function simple_list(?iterable $values, ?string $sep=", ", ?string $prefix=null, ?string $suffix=null): array {
|
||||||
return self::_list($values, $sep, $prefix, $suffix, null);
|
return self::_list($values, $sep, $prefix, $suffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
const LIST_SCHEMA = [
|
const LIST_SCHEMA = [
|
||||||
|
|||||||
33
src/web/content/BlockTag.php
Normal file
33
src/web/content/BlockTag.php
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\web\content;
|
||||||
|
|
||||||
|
class BlockTag extends Tag {
|
||||||
|
const START_PREFIX = null;
|
||||||
|
const START_SUFFIX = null;
|
||||||
|
|
||||||
|
const END_PREFIX = null;
|
||||||
|
const END_SUFFIX = "\n";
|
||||||
|
|
||||||
|
protected ?string $startPrefix;
|
||||||
|
protected ?string $startSuffix;
|
||||||
|
|
||||||
|
protected ?string $endPrefix;
|
||||||
|
protected ?string $endSuffix;
|
||||||
|
|
||||||
|
function reset(string $tag, ?array $params=null, $content=null): self {
|
||||||
|
parent::reset($tag, $params, $content);
|
||||||
|
$this->startPrefix = $params["start_prefix"] ?? self::START_PREFIX;
|
||||||
|
$this->startSuffix = $params["start_suffix"] ?? self::START_SUFFIX;
|
||||||
|
$this->endPrefix = $params["end_prefix"] ?? self::END_PREFIX;
|
||||||
|
$this->endSuffix = $params["end_suffix"] ?? self::END_SUFFIX;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStart(): array {
|
||||||
|
return [$this->startPrefix, ...parent::getStart(), $this->startSuffix];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEnd(): array {
|
||||||
|
return [$this->endPrefix, ...parent::getEnd(), $this->endSuffix];
|
||||||
|
}
|
||||||
|
}
|
||||||
6
src/web/content/EmptyTag.php
Normal file
6
src/web/content/EmptyTag.php
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\web\content;
|
||||||
|
|
||||||
|
class EmptyTag extends Tag {
|
||||||
|
const ALLOW_EMPTY = true;
|
||||||
|
}
|
||||||
@ -2,35 +2,179 @@
|
|||||||
namespace nulib\web\content;
|
namespace nulib\web\content;
|
||||||
|
|
||||||
|
|
||||||
|
use Closure;
|
||||||
use nulib\A;
|
use nulib\A;
|
||||||
|
use nulib\cl;
|
||||||
use nulib\php\content\c;
|
use nulib\php\content\c;
|
||||||
use nulib\php\content\IContent;
|
use nulib\php\content\IContent;
|
||||||
|
use nulib\php\content\IPrintable;
|
||||||
|
use nulib\php\func;
|
||||||
|
use nur\str;
|
||||||
|
|
||||||
class Tag implements IContent {
|
class Tag implements IContent, IPrintable {
|
||||||
function __construct(string $tag, $content=null) {
|
const ALLOW_EMPTY = false;
|
||||||
$this->tag = $tag;
|
|
||||||
$content = c::q($content);
|
const REQUIRE_CONTENT = false;
|
||||||
$this->content = $content;
|
|
||||||
|
function __construct(string $tag, ?array $params=null, $content=null) {
|
||||||
|
$this->reset($tag, $params, $content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected bool $allowEmpty;
|
||||||
|
|
||||||
|
protected bool $requireContent;
|
||||||
|
|
||||||
protected string $tag;
|
protected string $tag;
|
||||||
protected iterable $content;
|
|
||||||
|
|
||||||
function add($content): self {
|
protected iterable $contents;
|
||||||
if (!is_array($this->content)) {
|
|
||||||
# si c'est un itérable, l'inclure avec un merge statique, afin de pouvoir
|
protected ?array $attrs = null;
|
||||||
# rajouter des éléments
|
|
||||||
$this->content = [[[], $this->content]];
|
protected ?array $children = null;
|
||||||
}
|
|
||||||
A::merge($this->content, c::q($content));
|
protected bool $empty = false;
|
||||||
|
|
||||||
|
function reset(string $tag, ?array $params=null, $content=null): self {
|
||||||
|
$this->allowEmpty = $params["allow_empty"] ?? self::ALLOW_EMPTY;
|
||||||
|
$this->requireContent = $params["require_content"] ?? self::REQUIRE_CONTENT;
|
||||||
|
$this->tag = $tag;
|
||||||
|
$this->contents = c::q($content);
|
||||||
|
$this->attrs = null;
|
||||||
|
$this->children = null;
|
||||||
|
$this->empty = false;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getContent($object_or_class=null): iterable {
|
function add($content): self {
|
||||||
|
if (!is_array($this->contents)) {
|
||||||
|
# si c'est un itérable, l'inclure avec un merge statique, afin de pouvoir
|
||||||
|
# rajouter des éléments
|
||||||
|
$contents = $this->contents;
|
||||||
|
$this->contents = [static function() use ($contents) {
|
||||||
|
return $contents;
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
A::merge($this->contents, c::q($content));
|
||||||
|
$this->attrs = null;
|
||||||
|
$this->children = null;
|
||||||
|
$this->empty = false;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function add_contents(iterable $source, array &$contents, array &$attrs): void {
|
||||||
|
$index = 0;
|
||||||
|
foreach ($source as $key => $content) {
|
||||||
|
if ($content instanceof Closure) {
|
||||||
|
# les closure sont appelés dès la résolution
|
||||||
|
$content = func::with($content)->invoke([$this]);
|
||||||
|
if ($key === $index) {
|
||||||
|
$index++;
|
||||||
|
# le résultat est inséré tels quels dans le flux
|
||||||
|
if (is_iterable($content)) {
|
||||||
|
$contents[] = $content;
|
||||||
|
} else {
|
||||||
|
A::merge($contents, cl::with($content));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
# le résultat est la valeur de l'attribut
|
||||||
|
A::merge($attrs[$key], [$content]);
|
||||||
|
}
|
||||||
|
} elseif (is_array($content)) {
|
||||||
|
if ($key === $index) {
|
||||||
|
$index++;
|
||||||
|
$this->add_contents($content, $contents, $attrs);
|
||||||
|
} else {
|
||||||
|
foreach ($content as &$value) {
|
||||||
|
if ($value instanceof Closure) {
|
||||||
|
$value = func::with($value)->invoke([$this]);
|
||||||
|
}
|
||||||
|
}; unset($value);
|
||||||
|
A::merge($attrs[$key], $content);
|
||||||
|
}
|
||||||
|
} elseif ($key === $index) {
|
||||||
|
$index++;
|
||||||
|
A::merge($contents, cl::with($content));
|
||||||
|
} else {
|
||||||
|
A::merge($attrs[$key], [$content]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolve($objectOrClass=null): self {
|
||||||
|
if ($this->attrs === null) {
|
||||||
|
$attrs = [];
|
||||||
|
$contents = [];
|
||||||
|
$this->add_contents($this->contents, $contents, $attrs);
|
||||||
|
$this->attrs = $attrs;
|
||||||
|
$this->children = c::resolve($contents, $objectOrClass);
|
||||||
|
$this->empty = $this->allowEmpty && !$this->children;
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStart(): array {
|
||||||
|
$parts = ["<{$this->tag}"];
|
||||||
|
foreach ($this->attrs as $key => $value) {
|
||||||
|
if ($value === null || $value === false) continue;
|
||||||
|
if ($value === true || $value === [true]) {
|
||||||
|
$value = $key;
|
||||||
|
} elseif (is_array($value)) {
|
||||||
|
$value = str::join3($value);
|
||||||
|
if (!$value) continue;
|
||||||
|
}
|
||||||
|
$parts[] = " ";
|
||||||
|
$parts[] = $key;
|
||||||
|
$parts[] = "=\"";
|
||||||
|
$parts[] = htmlspecialchars($value);
|
||||||
|
$parts[] = "\"";
|
||||||
|
}
|
||||||
|
$parts[] = $this->empty? "/>": ">";
|
||||||
|
return $parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getChildren(): array {
|
||||||
|
return $this->children;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEnd(): array {
|
||||||
|
if ($this->empty) return [];
|
||||||
|
else return ["</{$this->tag}>"];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getContent($objectOrClass=null): iterable {
|
||||||
|
$this->resolve($objectOrClass);
|
||||||
|
if ($this->requireContent && !$this->children) return [];
|
||||||
return [
|
return [
|
||||||
"<$this->tag>",
|
...$this->getStart(),
|
||||||
...c::resolve($this->content, $object_or_class),
|
...$this->getChildren(),
|
||||||
"</$this->tag>",
|
...$this->getEnd(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** afficher le tag ouvrant. */
|
||||||
|
function printStart(): void {
|
||||||
|
$this->resolve();
|
||||||
|
c::write($this->getStart());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** afficher le contenu enfant */
|
||||||
|
function printChildren(): void {
|
||||||
|
$this->resolve();
|
||||||
|
c::write($this->getChildren());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** afficher le tag fermant */
|
||||||
|
function printEnd(): void {
|
||||||
|
$this->resolve();
|
||||||
|
c::write($this->getEnd());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** afficher le tag et le contenu enfant */
|
||||||
|
function print(): void {
|
||||||
|
$this->resolve();
|
||||||
|
if ($this->requireContent && !$this->children) return;
|
||||||
|
c::write($this->getStart());
|
||||||
|
c::write($this->getChildren());
|
||||||
|
c::write($this->getEnd());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,65 @@ namespace nulib\web\content;
|
|||||||
* Class v: classe outil pour gérer du contenu pour le web
|
* Class v: classe outil pour gérer du contenu pour le web
|
||||||
*/
|
*/
|
||||||
class v {
|
class v {
|
||||||
static function h1($content): iterable { return (new Tag("h1", $content))->getContent(); }
|
private const require_content = ["require_content" => true];
|
||||||
const h1 = [Tag::class, null, "h1"];
|
private const start_nl = ["start_suffix" => "\n"];
|
||||||
|
|
||||||
|
private static function h(string $tag, $content): BlockTag {
|
||||||
|
return new BlockTag($tag, self::require_content, $content);
|
||||||
|
}
|
||||||
|
static function h1($content): BlockTag { return self::h("h1", $content); }
|
||||||
|
const H1 = [BlockTag::class, false, "h1", self::require_content];
|
||||||
|
static function h2($content): BlockTag { return self::h("h2", $content); }
|
||||||
|
const H2 = [BlockTag::class, false, "h2", self::require_content];
|
||||||
|
static function h3($content): BlockTag { return self::h("h3", $content); }
|
||||||
|
const H3 = [BlockTag::class, false, "h3", self::require_content];
|
||||||
|
static function h4($content): BlockTag { return self::h("h4", $content); }
|
||||||
|
const H4 = [BlockTag::class, false, "h4", self::require_content];
|
||||||
|
static function h5($content): BlockTag { return self::h("h5", $content); }
|
||||||
|
const H5 = [BlockTag::class, false, "h5", self::require_content];
|
||||||
|
static function h6($content): BlockTag { return self::h("h6", $content); }
|
||||||
|
const H6 = [BlockTag::class, false, "h6", self::require_content];
|
||||||
|
|
||||||
|
static function hr($content): EmptyTag { return (new EmptyTag("hr", null, $content)); }
|
||||||
|
const HR = [EmptyTag::class, false, "hr", null];
|
||||||
|
static function br($content): EmptyTag { return (new EmptyTag("br", null, $content)); }
|
||||||
|
const BR = [EmptyTag::class, false, "br", null];
|
||||||
|
|
||||||
|
static function div($content): Tag { return (new Tag("div", null, $content)); }
|
||||||
|
const DIV = [Tag::class, false, "div", null];
|
||||||
|
static function p($content): Tag { return (new Tag("p", self::require_content, $content)); }
|
||||||
|
const P = [Tag::class, false, "p", self::require_content];
|
||||||
|
static function pre($content): Tag { return (new Tag("pre", self::require_content, $content)); }
|
||||||
|
const PRE = [Tag::class, false, "pre", self::require_content];
|
||||||
|
|
||||||
|
static function span($content): Tag { return (new Tag("span", null, $content)); }
|
||||||
|
const SPAN = [Tag::class, false, "span", null];
|
||||||
|
static function b($content): Tag { return (new Tag("b", null, $content)); }
|
||||||
|
const B = [Tag::class, false, "b", null];
|
||||||
|
static function i($content): Tag { return (new Tag("i", null, $content)); }
|
||||||
|
const I = [Tag::class, false, "i", null];
|
||||||
|
static function em($content): Tag { return (new Tag("em", null, $content)); }
|
||||||
|
const EM = [Tag::class, false, "em", null];
|
||||||
|
static function strong($content): Tag { return (new Tag("strong", null, $content)); }
|
||||||
|
const STRONG = [Tag::class, false, "strong", null];
|
||||||
|
|
||||||
|
static function ul($content): BlockTag { return (new BlockTag("ul", self::start_nl, $content)); }
|
||||||
|
const UL = [BlockTag::class, false, "ul", self::start_nl];
|
||||||
|
static function ol($content): BlockTag { return (new BlockTag("ol", self::start_nl, $content)); }
|
||||||
|
const OL = [BlockTag::class, false, "ol", self::start_nl];
|
||||||
|
static function li($content): BlockTag { return (new BlockTag("li", null, $content)); }
|
||||||
|
const LI = [BlockTag::class, false, "li", null];
|
||||||
|
|
||||||
|
static function table($content): BlockTag { return (new BlockTag("table", self::start_nl, $content)); }
|
||||||
|
const TABLE = [BlockTag::class, false, "table", self::start_nl];
|
||||||
|
static function thead($content): BlockTag { return (new BlockTag("thead", self::start_nl, $content)); }
|
||||||
|
const THEAD = [BlockTag::class, false, "thead", self::start_nl];
|
||||||
|
static function tbody($content): BlockTag { return (new BlockTag("tbody", self::start_nl, $content)); }
|
||||||
|
const TBODY = [BlockTag::class, false, "tbody", self::start_nl];
|
||||||
|
static function tr($content): BlockTag { return (new BlockTag("tr", null, $content)); }
|
||||||
|
const TR = [BlockTag::class, false, "tr", null];
|
||||||
|
static function th($content): Tag { return (new Tag("th", null, $content)); }
|
||||||
|
const TH = [Tag::class, false, "th", null];
|
||||||
|
static function td($content): Tag { return (new Tag("td", null, $content)); }
|
||||||
|
const TD = [Tag::class, false, "td", null];
|
||||||
}
|
}
|
||||||
|
|||||||
78
src/web/layout/README.md
Normal file
78
src/web/layout/README.md
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
# nulib\web\layout
|
||||||
|
|
||||||
|
faire le layout d'avance, e.g
|
||||||
|
~~~php
|
||||||
|
ly::prepare([
|
||||||
|
["row", "class" => "row-gap",
|
||||||
|
["col", 2, "content" => "a"],
|
||||||
|
["col", 10, "content" => "b"],
|
||||||
|
],
|
||||||
|
["row",
|
||||||
|
["col", 6, "content" => "c"],
|
||||||
|
["col", 6, "content" => "d"],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
~~~
|
||||||
|
dans cet exemple, il y a 4 sections de contenu appelées "a", "b", "c" et "d"
|
||||||
|
|
||||||
|
désactiver le contenu dans un colonne ou un row avec `"content" => false`
|
||||||
|
|
||||||
|
faut-il prévoir d'autres types que "row" et "col", par exemple "panel"?
|
||||||
|
|
||||||
|
une fois que le layout est fait, on sélectionne les sections avant de les remplir
|
||||||
|
~~~php
|
||||||
|
ly::start("a");
|
||||||
|
//... contenu de la section "a"
|
||||||
|
ly::start("b");
|
||||||
|
//... contenu de la section "b"
|
||||||
|
ly::end();
|
||||||
|
~~~
|
||||||
|
tant que les sections sont mentionnées dans l'ordre, l'affichage se fait au fur
|
||||||
|
et à mesure
|
||||||
|
|
||||||
|
*éventuellement*, supporter un mode où les sections sont remplies dans un ordre
|
||||||
|
quelconque. dans ce cas, le contenu est enregistré dans un fichier temporaire
|
||||||
|
mémoire avec `ob_start()` et `ob_end()` puis il est affiché à la fin lors de
|
||||||
|
ly::end()
|
||||||
|
exemple:
|
||||||
|
~~~php
|
||||||
|
ly::prepare([["row",
|
||||||
|
["col", 3, "content" => "menu"],
|
||||||
|
["col", 9, "content" => "details"],
|
||||||
|
]]);
|
||||||
|
foreach ($items as $item) {
|
||||||
|
ly::start("menu");
|
||||||
|
write(link);
|
||||||
|
ly::start("details");
|
||||||
|
write(details);
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
## alternatives
|
||||||
|
|
||||||
|
les ids de contenu sont des clés
|
||||||
|
~~~php
|
||||||
|
ly::prepare([["row",
|
||||||
|
"menu" => ["col", 3],
|
||||||
|
"details" => ["col", 9],
|
||||||
|
]]);
|
||||||
|
~~~
|
||||||
|
conflit possible avec `"class" => xxx` et autres attributs?
|
||||||
|
|
||||||
|
`ly::start($id, $func)` permet de basculer temporairement dans une section
|
||||||
|
~~~php
|
||||||
|
ly::start("details");
|
||||||
|
foreach ($items as $item) {
|
||||||
|
ly::start("menu", function() {
|
||||||
|
write(link);
|
||||||
|
});
|
||||||
|
write(details);
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
le comportement d'enregistrer le contenu devrait être demandé explicitement
|
||||||
|
* true
|
||||||
|
* false: exception si une section n'est pas remplie avant de passer à la suivante
|
||||||
|
* auto: activé si les sections sont accédées dans un ordre différent du naturel
|
||||||
|
|
||||||
|
-*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary
|
||||||
@ -1,9 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nulib\php\content;
|
namespace nulib\php\content;
|
||||||
|
|
||||||
use nulib\php\content\impl\html;
|
use nulib\php\content\impl\AContent;
|
||||||
|
use nulib\php\content\impl\APrintable;
|
||||||
|
use nulib\tests\TestCase;
|
||||||
use nulib\web\content\v;
|
use nulib\web\content\v;
|
||||||
use PHPUnit\Framework\TestCase;
|
|
||||||
|
|
||||||
class cTest extends TestCase {
|
class cTest extends TestCase {
|
||||||
function testTo_string() {
|
function testTo_string() {
|
||||||
@ -20,21 +21,24 @@ class cTest extends TestCase {
|
|||||||
self::assertSame("hello. world", c::to_string(["hello.", "world"]));
|
self::assertSame("hello. world", c::to_string(["hello.", "world"]));
|
||||||
self::assertSame("hello.<world>", c::to_string(["hello.", "<world>"]));
|
self::assertSame("hello.<world>", c::to_string(["hello.", "<world>"]));
|
||||||
|
|
||||||
self::assertSame(
|
self::assertSame(<<<EOT
|
||||||
"<h1>title<q/></h1><p>hello<nq/><span>brave<q/></span><span>world<nq/></span></p>",
|
<h1>title<q/></h1>
|
||||||
c::to_string([
|
<p>hello<nq/><span>brave<q/></span><span>world<nq/></span></p>
|
||||||
[html::H1, "title<q/>"],
|
EOT, c::to_string([
|
||||||
[html::P, [
|
[v::H1, "title<q/>"],
|
||||||
|
[v::P, [
|
||||||
"hello<nq/>",
|
"hello<nq/>",
|
||||||
[html::SPAN, "brave<q/>"],
|
[v::SPAN, "brave<q/>"],
|
||||||
[html::SPAN, ["world<nq/>"]],
|
[v::SPAN, ["world<nq/>"]],
|
||||||
]],
|
]],
|
||||||
]));
|
]));
|
||||||
}
|
|
||||||
|
|
||||||
function testXxx() {
|
self::assertSame(<<<EOT
|
||||||
$content = [[v::h1, "hello"]];
|
<span>content</span><p>printable</p>
|
||||||
self::assertSame("<h1>hello</h1>", c::to_string($content));
|
EOT, c::to_string([
|
||||||
|
new AContent(),
|
||||||
|
new APrintable(),
|
||||||
|
]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,23 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace nulib\php\content\impl;
|
|
||||||
|
|
||||||
use nulib\php\content\c;
|
|
||||||
use nulib\php\content\IContent;
|
|
||||||
|
|
||||||
class ATag implements IContent {
|
|
||||||
function __construct(string $tag, $content=null) {
|
|
||||||
$this->tag = $tag;
|
|
||||||
$this->content = $content;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected $tag;
|
|
||||||
protected $content;
|
|
||||||
|
|
||||||
function getContent(): iterable {
|
|
||||||
return [
|
|
||||||
"<$this->tag>",
|
|
||||||
...c::q($this->content),
|
|
||||||
"</$this->tag>",
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace nulib\php\content\impl;
|
|
||||||
|
|
||||||
class html {
|
|
||||||
const H1 = [self::class, "h1"];
|
|
||||||
const DIV = [self::class, "div"];
|
|
||||||
const P = [self::class, "p"];
|
|
||||||
const SPAN = [self::class, "span"];
|
|
||||||
|
|
||||||
static function h1($content) { return new ATag("h1", $content); }
|
|
||||||
static function div($content) { return new ATag("div", $content); }
|
|
||||||
static function p($content) { return new ATag("p", $content); }
|
|
||||||
static function span($content) { return new ATag("span", $content); }
|
|
||||||
}
|
|
||||||
86
tests/web/content/TagTest.php
Normal file
86
tests/web/content/TagTest.php
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\web\content;
|
||||||
|
|
||||||
|
use nulib\php\content\c;
|
||||||
|
use nulib\tests\TestCase;
|
||||||
|
|
||||||
|
class TagTest extends TestCase {
|
||||||
|
function testTag() {
|
||||||
|
$tag = new Tag("tag", null, [
|
||||||
|
"before",
|
||||||
|
"class" => "first",
|
||||||
|
["class" => "second"],
|
||||||
|
function () {
|
||||||
|
return 42;
|
||||||
|
},
|
||||||
|
"attr" => [
|
||||||
|
"static",
|
||||||
|
"true" => true,
|
||||||
|
"false" => false,
|
||||||
|
],
|
||||||
|
"after",
|
||||||
|
]);
|
||||||
|
|
||||||
|
self::assertSame([
|
||||||
|
null,
|
||||||
|
"<tag",
|
||||||
|
" ", "class", "=\"", "first second", "\"",
|
||||||
|
" ", "attr", "=\"", "static true", "\"",
|
||||||
|
">",
|
||||||
|
"before",
|
||||||
|
42,
|
||||||
|
"after",
|
||||||
|
"</tag>",
|
||||||
|
null,
|
||||||
|
], $tag->getContent());
|
||||||
|
|
||||||
|
self::assertSame('<tag class="first second" attr="static true">before 42 after</tag>', c::to_string($tag));
|
||||||
|
}
|
||||||
|
|
||||||
|
function testMerge() {
|
||||||
|
$tag = new Tag("tag", null, [
|
||||||
|
"class" => "first",
|
||||||
|
["class" => "second"],
|
||||||
|
["class" => function () {
|
||||||
|
return "third";
|
||||||
|
}],
|
||||||
|
"cond" => [
|
||||||
|
"base",
|
||||||
|
"ok" => true,
|
||||||
|
"ko" => false,
|
||||||
|
"dynok" => function () {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
"dynko" => function () {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
["plouf" => "base"],
|
||||||
|
["plouf" => [
|
||||||
|
"ok" => true,
|
||||||
|
"ko" => false,
|
||||||
|
]],
|
||||||
|
["plouf" => [
|
||||||
|
"dynok" => function () {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
"dynko" => function () {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
]],
|
||||||
|
]);
|
||||||
|
|
||||||
|
self::assertSame([
|
||||||
|
null,
|
||||||
|
"<tag",
|
||||||
|
" ", "class", "=\"", "first second third", "\"",
|
||||||
|
" ", "cond", "=\"", "base ok dynok", "\"",
|
||||||
|
" ", "plouf", "=\"", "base ok dynok", "\"",
|
||||||
|
">",
|
||||||
|
"</tag>",
|
||||||
|
null,
|
||||||
|
], $tag->getContent());
|
||||||
|
|
||||||
|
self::assertSame('<tag class="first second third" cond="base ok dynok" plouf="base ok dynok"></tag>', c::to_string($tag));
|
||||||
|
}
|
||||||
|
}
|
||||||
96
tests/web/content/vTest.php
Normal file
96
tests/web/content/vTest.php
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\web\content;
|
||||||
|
|
||||||
|
use nulib\cl;
|
||||||
|
use nulib\php\content\c;
|
||||||
|
use nulib\tests\TestCase;
|
||||||
|
|
||||||
|
class vTest extends TestCase {
|
||||||
|
function testStatic() {
|
||||||
|
$static = [
|
||||||
|
[v::H1, "title"],
|
||||||
|
[v::P, "text"],
|
||||||
|
];
|
||||||
|
self::assertSame(<<<EOT
|
||||||
|
<h1>title</h1>
|
||||||
|
<p>text</p>
|
||||||
|
EOT, c::to_string($static));
|
||||||
|
|
||||||
|
$static = [
|
||||||
|
[v::DIV, [
|
||||||
|
"before",
|
||||||
|
"class" => "div",
|
||||||
|
[v::SPAN, "spanned"],
|
||||||
|
"disabled" => false,
|
||||||
|
"checked" => true,
|
||||||
|
"after",
|
||||||
|
]],
|
||||||
|
];
|
||||||
|
self::assertSame(<<<EOT
|
||||||
|
<div class="div" checked="checked">before<span>spanned</span>after</div>
|
||||||
|
EOT, c::to_string($static));
|
||||||
|
}
|
||||||
|
|
||||||
|
function testDynamic() {
|
||||||
|
$dynamic = [
|
||||||
|
v::h1("title"),
|
||||||
|
v::p("text"),
|
||||||
|
];
|
||||||
|
self::assertSame(<<<EOT
|
||||||
|
<h1>title</h1>
|
||||||
|
<p>text</p>
|
||||||
|
EOT, c::to_string($dynamic));
|
||||||
|
|
||||||
|
$dynamic = [
|
||||||
|
v::div([
|
||||||
|
"before",
|
||||||
|
"class" => "div",
|
||||||
|
v::span("spanned"),
|
||||||
|
"disabled" => false,
|
||||||
|
"checked" => true,
|
||||||
|
"after",
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
self::assertSame(<<<EOT
|
||||||
|
<div class="div" checked="checked">before<span>spanned</span>after</div>
|
||||||
|
EOT, c::to_string($dynamic));
|
||||||
|
}
|
||||||
|
|
||||||
|
function testDynamic2() {
|
||||||
|
$rows = [
|
||||||
|
["a" => 1, "b" => 2, "c" => 3],
|
||||||
|
["a" => "un", "b" => "deux", "c" => "trois"],
|
||||||
|
["a" => "one", "b" => "two", "c" => "three"],
|
||||||
|
];
|
||||||
|
$dynamic = v::table([
|
||||||
|
v::thead(v::tr(function() use ($rows) {
|
||||||
|
$headers = array_keys(cl::first($rows));
|
||||||
|
foreach ($headers as $header) {
|
||||||
|
yield v::th($header);
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
v::tbody(function() use ($rows) {
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
yield v::tr(function () use ($row) {
|
||||||
|
foreach ($row as $col) {
|
||||||
|
yield v::td($col);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
self::assertSame(<<<EOT
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr><th>a</th><th>b</th><th>c</th></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td>1</td><td>2</td><td>3</td></tr>
|
||||||
|
<tr><td>un</td><td>deux</td><td>trois</td></tr>
|
||||||
|
<tr><td>one</td><td>two</td><td>three</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
EOT, c::to_string($dynamic));
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user