Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 51 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: CI

on:
push:
branches: [ main ]
branches: [ main, 5.x-dev ]
pull_request:
branches: [ main ]
branches: [ main, 5.x-dev ]

env:
PHP_EXTENSIONS: intl, pdo_sqlite
Expand All @@ -30,5 +30,53 @@ jobs:
- name: Run PHPStan
run: vendor/bin/phpstan analyse --no-progress --error-format=github

- name: Show PHPCS installed standards
run: vendor/bin/phpcs -i || true

- name: Ensure PHPCS sees Cake & Slevomat standards
run: |
vendor/bin/phpcs --config-set installed_paths \
vendor/cakephp/cakephp-codesniffer,vendor/slevomat/coding-standard
vendor/bin/phpcs -i

- name: Run PHPCS
run: vendor/bin/phpcs
run: vendor/bin/phpcs

tests:
name: PHP ${{ matrix.php }} • PHPUnit
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- php: '8.2'
experimental: false
- php: '8.3'
experimental: false
- php: '8.4'
experimental: true # allow failures if ecosystem lags
continue-on-error: ${{ matrix.experimental }}

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: ${{ env.PHP_EXTENSIONS }}
ini-values: |
memory_limit=512M
coverage: none

- name: Validate composer.json
run: composer validate --strict

- name: Install dependencies (cached)
uses: ramsey/composer-install@v3
with:
composer-options: --no-interaction --no-progress

- name: Run PHPUnit
run: vendor/bin/phpunit
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ document.body.addEventListener('htmx:configRequest', (event) => {
## Rendering blocks and OOB Swap
The `setBlock()` function allows you to render a specific block while removing other blocks that might be rendered. This is particularly useful when you need to update only a portion of your view.

Calling `setBlock(null)` clears any selection.

```php
$this->Htmx->setBlock('userTable');
```
Expand All @@ -130,9 +132,15 @@ The `addBlocks()` function allows you to add multiple blocks to the list of bloc
$this->Htmx->addBlocks(['userTable', 'pagination']);
$this->Htmx->addBlocks(['userTable', 'pagination'], true); // Appends the blocks to the existing array.
```
> **Note:** `addBlocks()` appends by default. Pass `false` as the second argument to replace:
>
> ```php
> $this->Htmx->addBlocks(['usersTable', 'pagination']); // append (default)
> $this->Htmx->addBlocks(['onlyThis'], false); // replace
> ```

### OOB Swap
Htmx supports updating multiple targets by returning multiple partial responses with [`hx-swap-oop`](https://htmx.org/docs/#oob_swaps).
Htmx supports updating multiple targets by returning multiple partial responses with [`hx-swap-oob`](https://htmx.org/docs/#oob_swaps).
See the example `Users index search functionality with pagination update`
Note if you are working with tables like in the example. You might need to add
```javascript
Expand All @@ -142,6 +150,17 @@ Note if you are working with tables like in the example. You might need to add
```
In your template or layout.


### Clearing Blocks
You can clear the current block selection in two equivalent ways:
```php
// Explicitly clear any selection
$this->Htmx->clearBlocks();

// Or, using setBlock(null)
$this->Htmx->setBlock(null);
```

## Examples

### Users index search functionality
Expand Down
8 changes: 5 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
"require-dev": {
"phpunit/phpunit": "^10.1",
"phpstan/phpstan": "^2.1",
"cakephp/cakephp-codesniffer": "^5.2"
"cakephp/cakephp-codesniffer": "^5.2",
"slevomat/coding-standard": "^8.15",
"dealerdirect/phpcodesniffer-composer-installer": "^1.0"
},
"autoload": {
"psr-4": {
Expand All @@ -19,8 +21,7 @@
},
"autoload-dev": {
"psr-4": {
"CakeHtmx\\Test\\": "tests/",
"Cake\\Test\\": "vendor/cakephp/cakephp/tests/"
"CakeHtmx\\Test\\": "tests/"
}
},
"config": {
Expand All @@ -29,6 +30,7 @@
}
},
"scripts": {
"test": "phpunit",
"stan": "phpstan analyse",
"cs-check": "phpcs --colors -p -s",
"cs-fix": "phpcbf --colors -p -s"
Expand Down
75 changes: 48 additions & 27 deletions src/Controller/Component/HtmxComponent.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,31 +91,33 @@ public function beforeRender(Event $event): void
*/
public function afterRender(Event $event): void
{
if (!empty($this->blocks)) {
/** @var \Cake\View\View $view */
$view = $event->getSubject();
// empty the content and replace with the ones we want
$view->assign('content', '');

$first = true;
foreach ($this->blocks as $block) {
if ($view->exists($block)) {
$fetchBlock = $view->fetch($block);

if (!$first) {
$fetchBlock = preg_replace(
'/(<[^\/][^>]*)(>)/',
'$1 hx-swap-oob="innerHTML"$2',
$fetchBlock,
1,
);
}

$view->append('content', $fetchBlock);

if ($first) {
$first = false;
}
if (empty($this->blocks)) {
return;
}

/** @var \Cake\View\View $view */
$view = $event->getSubject();
// empty the content and replace with the ones we want
$view->assign('content', '');

$first = true;
foreach ($this->blocks as $block) {
if ($view->exists($block)) {
$fetchBlock = $view->fetch($block);

if (!$first) {
$fetchBlock = preg_replace(
'/(<[^\/][^>]*)(>)/',
'$1 hx-swap-oob="innerHTML"$2',
$fetchBlock,
1,
);
}

$view->append('content', $fetchBlock);

if ($first) {
$first = false;
}
}
}
Expand Down Expand Up @@ -392,12 +394,13 @@ private function encodeTriggers(array $triggers): string
/**
* Set a specific block to render
* Removes other blocks that might be rendered
* Passing `null` will clear all blocks.
*
* @param string|null $block Name of the block
*/
public function setBlock(?string $block): static
{
$this->blocks = [$block];
$this->blocks = !empty($block) ? [$block] : [];

return $this;
}
Expand All @@ -420,8 +423,14 @@ public function addBlock(string $block): static
* @param array $blocks List of block names to render
* @param bool $append Whether to append the blocks or replace existing ones
*/
public function addBlocks(array $blocks, bool $append = false): static
public function addBlocks(array $blocks, bool $append = true): static
{
// Make sure no empty blocks
$blocks = array_values(array_filter(
$blocks,
static fn($b) => is_string($b) && $b !== '',
));

if ($append) {
$this->blocks = array_merge($this->blocks, $blocks);
} else {
Expand All @@ -440,4 +449,16 @@ public function getBlocks(): ?array
{
return $this->blocks;
}

/**
* Clear all blocks so none will be rendered.
*
* @return static
*/
public function clearBlocks(): static
{
$this->blocks = [];

return $this;
}
}
Loading
Loading