mirror of
https://github.com/filamentphp/filament.git
synced 2026-09-24 15:42:09 +08:00
Merge pull request #17783 from filamentphp/fix/partially-rendering-hidden-schema-components
fix: Partially rendering hidden schema components
This commit is contained in:
@@ -34,9 +34,11 @@ use Filament\Support\Concerns\CanGrow;
|
||||
use Filament\Support\Concerns\CanOrderColumns;
|
||||
use Filament\Support\Concerns\CanSpanColumns;
|
||||
use Filament\Support\Concerns\HasExtraAttributes;
|
||||
use Filament\Support\Enums\Width;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Livewire\Drawer\Utils;
|
||||
use Livewire\Exceptions\RootTagMissingFromViewException;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Js;
|
||||
use Illuminate\View\ComponentAttributeBag;
|
||||
|
||||
class Component extends ViewComponent
|
||||
{
|
||||
@@ -115,33 +117,6 @@ class Component extends ViewComponent
|
||||
};
|
||||
}
|
||||
|
||||
public function toHtml(): string
|
||||
{
|
||||
if ($this->isLiberatedFromContainerGrid()) {
|
||||
return parent::toHtml();
|
||||
}
|
||||
|
||||
$key = $this->getKey();
|
||||
|
||||
if (blank($key)) {
|
||||
return parent::toHtml();
|
||||
}
|
||||
|
||||
$html = parent::toHtml();
|
||||
|
||||
if (blank($html)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
try {
|
||||
return Utils::insertAttributesIntoHtmlRoot($html, [
|
||||
'wire:partial' => "schema-component::{$key}",
|
||||
]);
|
||||
} catch (RootTagMissingFromViewException) {
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
@@ -153,4 +128,101 @@ class Component extends ViewComponent
|
||||
'record' => $this->getRecord(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Do not use this method outside the internals of Filament. It is subject to breaking changes in minor and patch releases.
|
||||
*/
|
||||
public function toSchemaHtml(?bool $isVisible = null): string
|
||||
{
|
||||
$isVisible ??= $this->isVisible();
|
||||
|
||||
$container = $this->getContainer();
|
||||
|
||||
$isContainerEmbeddedInParentComponent = $container->isEmbeddedInParentComponent();
|
||||
$containerParentComponent = $isContainerEmbeddedInParentComponent
|
||||
? $container->getParentComponent()
|
||||
: null;
|
||||
$containerStatePath = $isContainerEmbeddedInParentComponent
|
||||
? $containerParentComponent->getContainer()->getStatePath()
|
||||
: $container->getStatePath();
|
||||
|
||||
/**
|
||||
* Instead of only rendering the hidden components, we should
|
||||
* render the `<div>` wrappers for all fields, regardless of
|
||||
* if they are hidden or not. This is to solve Livewire DOM
|
||||
* diffing issues.
|
||||
*
|
||||
* Additionally, any `<div>` elements that wrap hidden
|
||||
* components need to have `class="fi-hidden"`, so that they
|
||||
* don't consume grid space.
|
||||
*/
|
||||
$hiddenJs = $this->getHiddenJs();
|
||||
$visibleJs = $this->getVisibleJs();
|
||||
|
||||
$maxWidth = $this->getMaxWidth();
|
||||
|
||||
$statePath = $isContainerEmbeddedInParentComponent
|
||||
? $containerParentComponent->getStatePath()
|
||||
: $this->getStatePath();
|
||||
|
||||
$key = $this->getKey();
|
||||
|
||||
$attributes = (new ComponentAttributeBag)
|
||||
->when(
|
||||
! $container->isInline(),
|
||||
fn (ComponentAttributeBag $attributes) => $attributes->gridColumn($this->getColumnSpan(), $this->getColumnStart(), $this->getColumnOrder(), ! $isVisible),
|
||||
)
|
||||
->merge([
|
||||
'wire:key' => $this->getLivewireKey(),
|
||||
...(($pollingInterval = $this->getPollingInterval()) ? ["wire:poll.{$pollingInterval}" => "partiallyRenderSchemaComponent('{$this->getKey()}')"] : []),
|
||||
], escape: false)
|
||||
->class([
|
||||
($maxWidth instanceof Width) ? "fi-width-{$maxWidth->value}" : $maxWidth,
|
||||
]);
|
||||
|
||||
ob_start(); ?>
|
||||
|
||||
<div
|
||||
<?php if (filled($key)) { ?>
|
||||
wire:partial="schema-component::<?= $key ?>"
|
||||
<?php } ?>
|
||||
<?php if ($isVisible) { ?>
|
||||
x-data="filamentSchemaComponent({
|
||||
path: <?= Js::from($statePath) ?>,
|
||||
containerPath: <?= Js::from($containerStatePath) ?>,
|
||||
isLive: <?= Js::from($this->isLive()) ?>,
|
||||
$wire,
|
||||
})"
|
||||
<?php if ($afterStateUpdatedJs = $this->getAfterStateUpdatedJs()) { ?>
|
||||
x-init="<?= implode(';', array_map(
|
||||
fn (string $js): string => '$wire.watch(' . Js::from($statePath) . ', ($state, $old) => ($state !== undefined) && eval(' . Js::from($js) . '))',
|
||||
$afterStateUpdatedJs,
|
||||
)) ?>"
|
||||
<?php } ?>
|
||||
<?php if (filled($visibilityJs = match ([filled($hiddenJs), filled($visibleJs)]) {
|
||||
[true, true] => "(! ({$hiddenJs})) && ({$visibleJs})",
|
||||
[true, false] => "! ({$hiddenJs})",
|
||||
[false, true] => $visibleJs,
|
||||
default => null,
|
||||
})) { ?>
|
||||
x-bind:class="{ 'fi-hidden': ! (<?= $visibilityJs ?>) }"
|
||||
x-cloak
|
||||
<?php } ?>
|
||||
<?php } ?>
|
||||
<?= $attributes->toHtml() ?>
|
||||
>
|
||||
<?php if ($isVisible) { ?>
|
||||
<div
|
||||
class="<?= Arr::toCssClasses([
|
||||
'fi-sc-component',
|
||||
'fi-grid-ctn' => $this->isGridContainer(),
|
||||
]) ?>"
|
||||
>
|
||||
<?= $this->toHtml() ?>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
|
||||
<?php return ob_get_clean();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ trait CanPartiallyRender
|
||||
}
|
||||
|
||||
return [
|
||||
"schema-component::{$key}" => $this->hasView() ? $this->render() : $this->toHtml(...),
|
||||
"schema-component::{$key}" => $this->toSchemaHtml(...),
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
use InvalidArgumentException;
|
||||
use Livewire\Livewire;
|
||||
use LogicException;
|
||||
|
||||
@@ -168,7 +169,13 @@ trait HasState
|
||||
|
||||
if (filled($components = $this->getComponentsToPartiallyRenderAfterStateUpdated())) {
|
||||
foreach ($components as $key) {
|
||||
$this->getLivewire()->getSchemaComponent($this->resolveRelativeKey($key))->partiallyRender();
|
||||
$component = $this->getLivewire()->getSchemaComponent($this->resolveRelativeKey($key), withHidden: true);
|
||||
|
||||
if (! $component) {
|
||||
throw new InvalidArgumentException("Could not find component [{$key}] to partially render.");
|
||||
}
|
||||
|
||||
$component->partiallyRender();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,10 +13,8 @@ use Filament\Support\Concerns\HasAlignment;
|
||||
use Filament\Support\Concerns\HasDefaultDataFormattingSettings;
|
||||
use Filament\Support\Concerns\HasExtraAttributes;
|
||||
use Filament\Support\Enums\Alignment;
|
||||
use Filament\Support\Enums\Width;
|
||||
use Illuminate\Contracts\Support\Htmlable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Js;
|
||||
use Illuminate\View\ComponentAttributeBag;
|
||||
use Livewire\Component as LivewireComponent;
|
||||
@@ -175,14 +173,6 @@ class Schema extends ViewComponent implements HasEmbeddedView
|
||||
$isInline = $this->isInline();
|
||||
$isRoot = $this->isRoot();
|
||||
|
||||
$isEmbeddedInParentComponent = $this->isEmbeddedInParentComponent();
|
||||
$parentComponent = $isEmbeddedInParentComponent
|
||||
? $this->getParentComponent()
|
||||
: null;
|
||||
$statePath = $isEmbeddedInParentComponent
|
||||
? $parentComponent->getContainer()->getStatePath()
|
||||
: $this->getStatePath();
|
||||
|
||||
$attributes = $this->getExtraAttributeBag()
|
||||
->when(
|
||||
! $isInline,
|
||||
@@ -212,76 +202,7 @@ class Schema extends ViewComponent implements HasEmbeddedView
|
||||
<?php } ?>
|
||||
</div>
|
||||
<?php } elseif (! $schemaComponent->isLiberatedFromContainerGrid()) { ?>
|
||||
<?php
|
||||
/**
|
||||
* Instead of only rendering the hidden components, we should
|
||||
* render the `<div>` wrappers for all fields, regardless of
|
||||
* if they are hidden or not. This is to solve Livewire DOM
|
||||
* diffing issues.
|
||||
*
|
||||
* Additionally, any `<div>` elements that wrap hidden
|
||||
* components need to have `class="fi-hidden"`, so that they
|
||||
* don't consume grid space.
|
||||
*/
|
||||
$hiddenJs = $schemaComponent->getHiddenJs();
|
||||
$visibleJs = $schemaComponent->getVisibleJs();
|
||||
|
||||
$maxWidth = $schemaComponent->getMaxWidth();
|
||||
|
||||
$schemaComponentStatePath = $isEmbeddedInParentComponent
|
||||
? $parentComponent->getStatePath()
|
||||
: $schemaComponent->getStatePath();
|
||||
|
||||
$attributes = (new ComponentAttributeBag)
|
||||
->when(
|
||||
! $isInline,
|
||||
fn (ComponentAttributeBag $attributes) => $attributes->gridColumn($schemaComponent->getColumnSpan(), $schemaComponent->getColumnStart(), $schemaComponent->getColumnOrder(), ! $isSchemaComponentVisible),
|
||||
)
|
||||
->merge([
|
||||
'wire:key' => $schemaComponent->getLivewireKey(),
|
||||
...(($pollingInterval = $schemaComponent->getPollingInterval()) ? ["wire:poll.{$pollingInterval}" => "partiallyRenderSchemaComponent('{$schemaComponent->getKey()}')"] : []),
|
||||
], escape: false)
|
||||
->class([
|
||||
($maxWidth instanceof Width) ? "fi-width-{$maxWidth->value}" : $maxWidth,
|
||||
]);
|
||||
?>
|
||||
<div
|
||||
<?php if ($isSchemaComponentVisible) { ?>
|
||||
x-data="filamentSchemaComponent({
|
||||
path: <?= Js::from($schemaComponentStatePath) ?>,
|
||||
containerPath: <?= Js::from($statePath) ?>,
|
||||
isLive: <?= Js::from($schemaComponent->isLive()) ?>,
|
||||
$wire,
|
||||
})"
|
||||
<?php if ($afterStateUpdatedJs = $schemaComponent->getAfterStateUpdatedJs()) { ?>
|
||||
x-init="<?= implode(';', array_map(
|
||||
fn (string $js): string => '$wire.watch(' . Js::from($schemaComponentStatePath) . ', ($state, $old) => ($state !== undefined) && eval(' . Js::from($js) . '))',
|
||||
$afterStateUpdatedJs,
|
||||
)) ?>"
|
||||
<?php } ?>
|
||||
<?php if (filled($visibilityJs = match ([filled($hiddenJs), filled($visibleJs)]) {
|
||||
[true, true] => "(! ({$hiddenJs})) && ({$visibleJs})",
|
||||
[true, false] => "! ({$hiddenJs})",
|
||||
[false, true] => $visibleJs,
|
||||
default => null,
|
||||
})) { ?>
|
||||
x-bind:class="{ 'fi-hidden': ! (<?= $visibilityJs ?>) }"
|
||||
x-cloak
|
||||
<?php } ?>
|
||||
<?php } ?>
|
||||
<?= $attributes->toHtml() ?>
|
||||
>
|
||||
<?php if ($isSchemaComponentVisible) { ?>
|
||||
<div
|
||||
class="<?= Arr::toCssClasses([
|
||||
'fi-sc-component',
|
||||
'fi-grid-ctn' => $schemaComponent->isGridContainer(),
|
||||
]) ?>"
|
||||
>
|
||||
<?= $schemaComponent->toHtml() ?>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<?= $schemaComponent->toSchemaHtml(isVisible: $isSchemaComponentVisible) ?>
|
||||
<?php } elseif ($isSchemaComponentVisible) { ?>
|
||||
<?= $schemaComponent->toHtml() ?>
|
||||
<?php } ?>
|
||||
|
||||
Reference in New Issue
Block a user