Skip to content

Commit be34b13

Browse files
committed
feat(view): introduce parameter metadata
1 parent 15c303e commit be34b13

15 files changed

+262
-0
lines changed

packages/http/src/Session/CsrfTokenComponent.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Tempest\View\Elements\ViewComponentElement;
88
use Tempest\View\ViewComponent;
9+
use Tempest\View\ViewComponentParameters;
910

1011
final readonly class CsrfTokenComponent implements ViewComponent
1112
{
@@ -14,6 +15,11 @@ public static function getName(): string
1415
return 'x-csrf-token';
1516
}
1617

18+
public static function getParameters(): ViewComponentParameters
19+
{
20+
return new ViewComponentParameters();
21+
}
22+
1723
public function compile(ViewComponentElement $element): string
1824
{
1925
$name = Session::CSRF_TOKEN_KEY;

packages/view/src/Components/AnonymousViewComponent.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Tempest\View\Elements\ViewComponentElement;
88
use Tempest\View\ViewComponent;
9+
use Tempest\View\ViewComponentParameters;
910

1011
final readonly class AnonymousViewComponent implements ViewComponent
1112
{
@@ -19,6 +20,11 @@ public static function getName(): string
1920
return 'x-component';
2021
}
2122

23+
public static function getParameters(): ViewComponentParameters
24+
{
25+
return new ViewComponentParameters();
26+
}
27+
2228
public function compile(ViewComponentElement $element): string
2329
{
2430
return $this->contents;

packages/view/src/Components/DynamicViewComponent.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
use Tempest\View\Parser\TempestViewCompiler;
1111
use Tempest\View\Parser\Token;
1212
use Tempest\View\ViewComponent;
13+
use Tempest\View\ViewComponentParameter;
14+
use Tempest\View\ViewComponentParameters;
1315
use Tempest\View\ViewConfig;
1416

1517
use function Tempest\Support\arr;
@@ -30,6 +32,22 @@ public static function getName(): string
3032
return 'x-dynamic-component';
3133
}
3234

35+
public static function getParameters(): ViewComponentParameters
36+
{
37+
return new ViewComponentParameters(
38+
new ViewComponentParameter(
39+
name: 'is',
40+
required: false,
41+
description: 'The name of the component to render dynamically.',
42+
),
43+
new ViewComponentParameter(
44+
name: ':is',
45+
required: false,
46+
description: 'An expression that resolves to the name of the component to render dynamically.',
47+
),
48+
);
49+
}
50+
3351
public function compile(ViewComponentElement $element): string
3452
{
3553
$name = $this->token->getAttribute('is') ?? $this->token->getAttribute(':is');

packages/view/src/Components/Form.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
use Tempest\View\Elements\ViewComponentElement;
88
use Tempest\View\ViewComponent;
9+
use Tempest\View\ViewComponentParameter;
10+
use Tempest\View\ViewComponentParameters;
911

1012
final readonly class Form implements ViewComponent
1113
{
@@ -14,6 +16,26 @@ public static function getName(): string
1416
return 'x-form';
1517
}
1618

19+
public static function getParameters(): ViewComponentParameters
20+
{
21+
return new ViewComponentParameters(
22+
new ViewComponentParameter(
23+
name: 'action',
24+
description: 'The URL to which the form will be submitted.',
25+
),
26+
new ViewComponentParameter(
27+
name: 'method',
28+
description: 'The HTTP method to use when submitting the form.',
29+
default: 'post',
30+
),
31+
new ViewComponentParameter(
32+
name: 'enctype',
33+
description: 'The encoding type for the form data.',
34+
default: '',
35+
),
36+
);
37+
}
38+
1739
public function compile(ViewComponentElement $element): string
1840
{
1941
$action = $element->getAttribute('action');

packages/view/src/Components/Icon.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
use Tempest\Support\Str\ImmutableString;
1212
use Tempest\View\Elements\ViewComponentElement;
1313
use Tempest\View\ViewComponent;
14+
use Tempest\View\ViewComponentParameter;
15+
use Tempest\View\ViewComponentParameters;
1416

1517
final readonly class Icon implements ViewComponent
1618
{
@@ -24,6 +26,21 @@ public static function getName(): string
2426
return 'x-icon';
2527
}
2628

29+
public static function getParameters(): ViewComponentParameters
30+
{
31+
return new ViewComponentParameters(
32+
new ViewComponentParameter(
33+
name: 'name',
34+
required: true,
35+
description: 'The name of the icon to render.',
36+
),
37+
new ViewComponentParameter(
38+
name: 'class',
39+
description: 'Optional CSS class(es) to apply to the icon.',
40+
),
41+
);
42+
}
43+
2744
public function compile(ViewComponentElement $element): string
2845
{
2946
if (! $element->hasAttribute('name')) {

packages/view/src/Components/Input.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
use Tempest\Validation\Rule;
99
use Tempest\View\Elements\ViewComponentElement;
1010
use Tempest\View\ViewComponent;
11+
use Tempest\View\ViewComponentParameter;
12+
use Tempest\View\ViewComponentParameters;
1113

1214
final readonly class Input implements ViewComponent
1315
{
@@ -20,6 +22,28 @@ public static function getName(): string
2022
return 'x-input';
2123
}
2224

25+
public static function getParameters(): ViewComponentParameters
26+
{
27+
return new ViewComponentParameters(
28+
new ViewComponentParameter(
29+
name: 'name',
30+
description: 'The name of the input field.',
31+
),
32+
new ViewComponentParameter(
33+
name: 'label',
34+
description: 'The label for the input field.',
35+
),
36+
new ViewComponentParameter(
37+
name: 'type',
38+
description: 'The type of the input field (e.g., text, email, password, textarea).',
39+
),
40+
new ViewComponentParameter(
41+
name: 'default',
42+
description: 'The default value for the input field.',
43+
),
44+
);
45+
}
46+
2347
public function compile(ViewComponentElement $element): string
2448
{
2549
$name = $element->getAttribute('name');

packages/view/src/Components/Submit.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
use Tempest\View\Elements\ViewComponentElement;
88
use Tempest\View\ViewComponent;
9+
use Tempest\View\ViewComponentParameter;
10+
use Tempest\View\ViewComponentParameters;
911

1012
final readonly class Submit implements ViewComponent
1113
{
@@ -14,6 +16,17 @@ public static function getName(): string
1416
return 'x-submit';
1517
}
1618

19+
public static function getParameters(): ViewComponentParameters
20+
{
21+
return new ViewComponentParameters(
22+
new ViewComponentParameter(
23+
name: 'label',
24+
description: 'The label for the submit button.',
25+
default: 'Submit',
26+
),
27+
);
28+
}
29+
1730
public function compile(ViewComponentElement $element): string
1831
{
1932
$label = $element->getAttribute('label') ?? 'Submit';

packages/view/src/ViewComponent.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,7 @@ interface ViewComponent
1010
{
1111
public static function getName(): string;
1212

13+
public static function getParameters(): ViewComponentParameters;
14+
1315
public function compile(ViewComponentElement $element): string;
1416
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\View;
6+
7+
use JsonSerializable;
8+
9+
final readonly class ViewComponentParameter
10+
{
11+
public function __construct(
12+
public string $name,
13+
public bool $required = false,
14+
public ?string $description = null,
15+
public mixed $default = null,
16+
) {}
17+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\View;
6+
7+
use ArrayAccess;
8+
use ArrayIterator;
9+
use BadMethodCallException;
10+
use Countable;
11+
use IteratorAggregate;
12+
use JsonSerializable;
13+
14+
final class ViewComponentParameters implements ArrayAccess, IteratorAggregate, Countable, JsonSerializable
15+
{
16+
/** @var ViewComponentParameter[] */
17+
private readonly array $parameters;
18+
19+
public function __construct(ViewComponentParameter ...$parameters)
20+
{
21+
$this->parameters = $parameters;
22+
}
23+
24+
/** @var ViewComponentParameter[] */
25+
public array $required {
26+
get => array_filter($this->parameters, static fn (ViewComponentParameter $parameter) => $parameter->required);
27+
}
28+
29+
/** @var ViewComponentParameter[] */
30+
public array $optional {
31+
get => array_filter($this->parameters, static fn (ViewComponentParameter $parameter) => ! $parameter->required);
32+
}
33+
34+
public function offsetExists(mixed $offset): bool
35+
{
36+
return isset($this->parameters[$offset]);
37+
}
38+
39+
public function offsetGet(mixed $offset): ?ViewComponentParameter
40+
{
41+
return $this->parameters[$offset] ?? null;
42+
}
43+
44+
public function offsetSet(mixed $offset, mixed $value): void
45+
{
46+
throw new BadMethodCallException('ViewComponentParameters is readonly');
47+
}
48+
49+
public function offsetUnset(mixed $offset): void
50+
{
51+
throw new BadMethodCallException('ViewComponentParameters is readonly');
52+
}
53+
54+
/**
55+
* @return ArrayIterator<ViewComponentParameter>
56+
*/
57+
public function getIterator(): ArrayIterator
58+
{
59+
return new ArrayIterator($this->parameters);
60+
}
61+
62+
public function count(): int
63+
{
64+
return count($this->parameters);
65+
}
66+
67+
/** @return ViewComponentParameter[] */
68+
public function toArray(): array
69+
{
70+
return $this->parameters;
71+
}
72+
73+
public function getByName(string $name): ?ViewComponentParameter
74+
{
75+
return array_find($this->parameters, static fn (ViewComponentParameter $parameter) => $parameter->name === $name);
76+
}
77+
78+
public function hasParameter(string $name): bool
79+
{
80+
return $this->getByName($name) !== null;
81+
}
82+
83+
public function jsonSerialize(): array
84+
{
85+
return $this->parameters;
86+
}
87+
}

0 commit comments

Comments
 (0)