Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/webchemistry/stimulus
https://github.com/webchemistry/stimulus
Last synced: about 1 month ago
JSON representation
- Host: GitHub
- URL: https://github.com/webchemistry/stimulus
- Owner: WebChemistry
- Created: 2021-09-16T10:45:30.000Z (over 3 years ago)
- Default Branch: master
- Last Pushed: 2024-09-02T13:15:53.000Z (4 months ago)
- Last Synced: 2024-09-16T02:49:37.921Z (3 months ago)
- Language: PHP
- Size: 68.4 KB
- Stars: 0
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: readme.md
Awesome Lists containing this project
README
## Installation
`composer require webchemistry/stimulus`
```neon
extensions:
- WebChemistry\Stimulus\DI\StimulusExtension
```## Set up extractor and generators
First, set up extractor and generator. [Namespaced](https://stimulus.hotwired.dev/handbook/installing#controller-filenames-map-to-identifiers)
identifiers also supported.Code with comments (copy-paste code is below):
```phprequire __DIR__ . '/vendor/autoload.php';
// directory with controllers, only *_controller.js and *-controller.js are extracted
$extractor = new JavascriptSourceExtractor(__DIR__ . '/js/stimulus/controllers');// optional, we want UI namespace instead of Ui
$keywords = ['ui' => 'UI'];// generate classes as Stimulus\*\_*Controller, these classes we don't edit
$originalClassNameConverter = new PrependClassNameConverter(
'Stimulus\\',
new AppendClassNameConverter(
new BaseClassNameConverter($keywords, fn (string $className) => '_' . $className),
'Controller',
),
);// We want edit these classes
$emptyClassNameConverter = new PrependClassNameConverter(
'App\\Stimulus\\Controller\\',
new AppendClassNameConverter(
new BaseClassNameConverter($keywords),
'Controller',
),
);// for autocomplete_controller.js these controllers are generated
// class Stimulus\_AutocompleteController
// class App\Stimulus\Controller\AutocompleteController extends Stimulus\_AutocompleteController// for namespace/autocomplete_controller.js these controllers are generated
// class Stimulus\Namespace\_AutocompleteController
// class App\Stimulus\Controller\Namespace\AutocompleteController extends Stimulus\_AutocompleteController// Generator generates static methods
$generator = new StaticClassStimulusControllerGenerator($extractor, $originalClassNameConverter);// Generator generates class with empty body and extends original class (Stimulus\*\_*Controller)
$emptyGenerator = new EmptyClassStimulusControllerGenerator(
$extractor,
$emptyClassNameConverter,
$originalClassNameConverter,
);// Files are written in app/generated/stimulus/*, Stimulus\ namespace have to be removed from path
$writer = new FilesystemWriter(__DIR__ . '/app/generated/stimulus', 'Stimulus\\');
// Files are written in app/src/Stimulus/Controller/*, App\Stimulus\Controller\ namespace have to be removed from path, if file exists don't rewrite it
$emptyWriter = new FilesystemWriter(__DIR__ . '/app/src/Stimulus/Controller', 'App\\Stimulus\\Controller\\', false);foreach ($generator->generate() as $generated) {
$writer->write($generated);
}foreach ($emptyGenerator->generate() as $generated) {
$emptyWriter->write($generated);
}```
copy-paste code:
```php
require __DIR__ . '/vendor/autoload.php';$extractor = new JavascriptSourceExtractor(__DIR__ . '/js/stimulus/controllers');
$keywords = ['ui' => 'UI'];
$originalClassNameConverter = new PrependClassNameConverter(
'Stimulus\\',
new AppendClassNameConverter(
new BaseClassNameConverter($keywords, fn (string $className) => '_' . $className),
'Controller',
),
);
$emptyClassNameConverter = new PrependClassNameConverter(
'App\\Stimulus\\Controller\\',
new AppendClassNameConverter(
new BaseClassNameConverter($keywords),
'Controller',
),
);$generator = new StaticClassStimulusControllerGenerator($extractor, $originalClassNameConverter);
$emptyGenerator = new EmptyClassStimulusControllerGenerator(
$extractor,
$emptyClassNameConverter,
$originalClassNameConverter,
);$writer = new FilesystemWriter(__DIR__ . '/app/generated/stimulus', 'Stimulus\\');
$emptyWriter = new FilesystemWriter(__DIR__ . '/app/src/Stimulus/Controller', 'App\\Stimulus\\Controller\\', false);foreach ($generator->generate() as $generated) {
$writer->write($generated);
}foreach ($emptyGenerator->generate() as $generated) {
$emptyWriter->write($generated);
}```
## How files are generated
Class `JavascriptSourceExtractor` uses javascript comments for generating.
Each controller must be annotated with `@controller`, actions with `@action` and their parameters with `@param`, values, classes and targets with `@property`.my_controller.js
```js
/**
* @controller
*
* @property {String} stringValue
*
* @property {HTMLElement[]} itemTargets
* @property {HTMLElement} resultsTarget
*
* @property {String} activeClass
*/
export default class extends Controller {
static targets = ['results', 'item'];
static values = {
string: String,
};
static classes = ['active'];/**
* @action
*/
switch() {
}
}```
This PHP class is generated:
```php
declare(strict_types = 1);/**
* NOTE: This class is auto generated by file: my_controller.js
* Do not edit the class manually
*/namespace Stimulus;
use WebChemistry\Stimulus\Type\StimulusAction;
use WebChemistry\Stimulus\Type\StimulusController;
use WebChemistry\Stimulus\Type\StimulusTarget;abstract class _MyController
{final public const identifier = 'my';
public static function construct(string $stringValue, string $activeClass): StimulusController
{
return new StimulusController(self::identifier, [
'stringValue' => $stringValue,
'activeClass' => $activeClass,
], []);
}
public static function itemTarget(): StimulusTarget
{
return new StimulusTarget(self::identifier, 'itemTarget');
}
public static function switchAction(): StimulusAction
{
return new StimulusAction(self::identifier, 'switch', []);
}
}
```
By default, each property is required, if we want to make optional we have
several ways:1. add `?` to the end of property name: `@property {String} stringValue?`
2. add `{ optional }` to the 3rd section (options) of property: `@property {String} stringValue {optional}`
3. add hasser `@property {Boolean} hasStringValue`## Javascript types and PHP types
webchemisty/stimulus introduces stricter environment for writting application.
Nowadays, everyone use static analysis (at least they should) so correct types are crucial.Library converts javascript types in the following way:
`Number` => `int|float` narrowing is achieved by options (3rd section) ` { number: int } ` \
`Array` and `Object` => `mixed[]` \
`Boolean` => `bool` \
`String` => `string` \
`other` => `mixed`Arrays: \
`String[]` => `string[]` \
`Number[]` => `array` narrowing: `{ number: float }` \
`Bool[]` => `bool[]` \
`other` => `mixed[]`## Custom types
Sometimes we need overriding comment types `@param ...` and types `method(... $type)`
fot this there is options `type` and `commentType` e.g. ` { type: mixed, commentType: "array" } `## Action parameters
Stimulus 3 introduced [parameters](https://stimulus.hotwired.dev/reference/actions#action-parameters) for actions.
For generating just use intersection type or just object type.```js
export default class extends Controller {
/**
* @action
* @param { { params: { value: String } } & PointerEvent} event
*/
switch(event) {
const { value } = event.params;
}
/**
* @action
* @param { { params: { value: String } } } event
*/
switchTwo(event) {
const { value } = event.params;
}
}
```## Usage in PHP
```php
use WebChemistry\Stimulus\Renderer\HtmlRenderer;$htmlAttributes = HtmlRenderer::render(
App\Stimulus\Controller\MyController::construct('string', 'activeClass'),
App\Stimulus\Controller\MyController::switchAction()->event('click'),
); // data-controller="my" data-my-string-value="string" data-my-active-class="active" data-action="click->my#switch"// or as array [attribute => value]
HtmlRenderer::toArray(...);
```## Usage in Latte
```html
```## Controller as service
Sometimes we want to inject other services:
```php
declare(strict_types = 1);namespace App\Stimulus\Controller;
use Stimulus\_MyController as ParentController;
use WebChemistry\Stimulus\Type\StimulusController;final class MyController extends ParentController
{public function __construct(
private LinkGenerator $linkGenerator,
) {}
public function doConstruct(): StimulusController
{
return self::construct($this->linkGenerator->link('...'));
}}
```