mirror of
https://github.com/filamentphp/filament.git
synced 2026-10-25 10:04:54 +08:00
Add stacking to images
This commit is contained in:
@@ -76,3 +76,71 @@ use Filament\Tables\Columns\ImageColumn;
|
||||
ImageColumn::make('logo')
|
||||
->extraImgAttributes(['title' => 'Company logo']),
|
||||
```
|
||||
|
||||
## Stacking images
|
||||
|
||||
You may show a relationship of images as a stack using `stacked()`. The name of the relationship comes first, followed by a period, followed by the name of the column to display:
|
||||
|
||||
```php
|
||||
ImageColumn::make('users.avatar')
|
||||
->circular()
|
||||
->stacked()
|
||||
```
|
||||
|
||||
### Using a separator
|
||||
|
||||
Instead of using a relationship, you may use a separated string by passing the separator into `separator()`:
|
||||
|
||||
```php
|
||||
ImageColumn::make('product_images')
|
||||
->separator(',')
|
||||
->circular()
|
||||
->stacked()
|
||||
```
|
||||
|
||||
### Setting a limit
|
||||
|
||||
You may set a limit to the number of images you want to display by passing `limit()`:
|
||||
|
||||
```php
|
||||
ImageColumn::make('orderItems.image')
|
||||
->circular()
|
||||
->stacked()
|
||||
->limit(3)
|
||||
```
|
||||
|
||||
### Showing the remaining images count
|
||||
|
||||
When you set a limit you may also display the count of remaining images by passing `showRemaining()`.
|
||||
|
||||
```php
|
||||
ImageColumn::make('orderItems.image')
|
||||
->circular()
|
||||
->stacked()
|
||||
->limit(3)
|
||||
->showRemaining()
|
||||
```
|
||||
|
||||
By default, `showRemaining()` will display the count of remaining images as a number stacked on the other images. If you prefer to show the count as a number after the images you may use `showRemainingAfterStack()`. You may also set the text size by using `remainingTextSize('xs')`;
|
||||
|
||||
### Customizing the ring width
|
||||
|
||||
The default ring width is `ring-3` but you may customize the ring width to be either `0`, `1`, `2`, or `4` which correspond to tailwinds `ring-widths`: `ring-0`, `ring-1`, `ring-2`, and `ring-4` respectively.
|
||||
|
||||
```php
|
||||
ImageColumn::make('users.avatar')
|
||||
->circular()
|
||||
->stacked()
|
||||
->ring(3)
|
||||
```
|
||||
|
||||
### Customizing the overlap
|
||||
|
||||
The default overlap is `-space-x-1` but you may customize the overlap to be either `0`, `1`, `2`, `3`, or `4` which correspond to tailwinds `space-x` options: `space-x-0`, `-space-x-1`, `-space-x-2`, `-space-x-3`, and `-space-x-4` respectively.
|
||||
|
||||
```php
|
||||
ImageColumn::make('users.avatar')
|
||||
->circular()
|
||||
->stacked()
|
||||
->overlap(3)
|
||||
```
|
||||
@@ -5,29 +5,120 @@
|
||||
@php
|
||||
$height = $getHeight();
|
||||
$width = $getWidth() ?? ($isCircular() || $isSquare() ? $height : null);
|
||||
$overlap = $getOverlap() ?? 'sm';
|
||||
$imageCount = 0;
|
||||
|
||||
$ring = match ($getRing()) {
|
||||
0 => 'ring-0',
|
||||
1 => 'ring-1',
|
||||
2 => 'ring-2',
|
||||
4 => 'ring-4',
|
||||
default => 'ring',
|
||||
};
|
||||
|
||||
$remainingTextSize = match ($getRemainingTextSize()) {
|
||||
'xs' => 'text-xs',
|
||||
'sm' => 'text-sm',
|
||||
'md' => 'text-md',
|
||||
'lg' => 'text-lg',
|
||||
default => 'text-sm',
|
||||
};
|
||||
@endphp
|
||||
|
||||
<div
|
||||
style="
|
||||
{!! $height !== null ? "height: {$height};" : null !!}
|
||||
{!! $width !== null ? "width: {$width};" : null !!}
|
||||
"
|
||||
@class([
|
||||
'overflow-hidden' => $isCircular() || $isSquare(),
|
||||
'rounded-full' => $isCircular(),
|
||||
])
|
||||
>
|
||||
@if ($path = $getImagePath())
|
||||
<img
|
||||
src="{{ $path }}"
|
||||
style="
|
||||
{!! $height !== null ? "height: {$height};" : null !!}
|
||||
{!! $width !== null ? "width: {$width};" : null !!}
|
||||
"
|
||||
{{ $getExtraImgAttributeBag()->class([
|
||||
'object-cover object-center' => $isCircular() || $isSquare(),
|
||||
]) }}
|
||||
@if ($isStacked())
|
||||
<div class="flex items-center space-x-2">
|
||||
<div
|
||||
@class([
|
||||
'flex',
|
||||
match ($overlap) {
|
||||
0 => 'space-x-0',
|
||||
1 => '-space-x-1',
|
||||
2 => '-space-x-2',
|
||||
3 => '-space-x-3',
|
||||
4 => '-space-x-4',
|
||||
default => '-space-x-1',
|
||||
},
|
||||
])
|
||||
>
|
||||
@endif
|
||||
</div>
|
||||
@foreach (array_slice($getImages(), 0, $getLimit()) as $image)
|
||||
@if ($path = $getImagePath($image))
|
||||
@php
|
||||
$imageCount ++;
|
||||
@endphp
|
||||
<img
|
||||
src="{{ $path }}"
|
||||
style="
|
||||
{!! $height !== null ? "height: {$height};" : null !!}
|
||||
{!! $width !== null ? "width: {$width};" : null !!}
|
||||
"
|
||||
{{ $getExtraImgAttributeBag()->class([
|
||||
'max-w-none ring-white',
|
||||
'dark:ring-gray-800' => config('tables.dark_mode'),
|
||||
'rounded-full' => $isCircular(),
|
||||
$ring,
|
||||
]) }}
|
||||
>
|
||||
@endif
|
||||
@endforeach
|
||||
|
||||
@if ($shouldShowRemaining() && (! $shouldShowRemainingAfterStack()) && ($imageCount < count($getImages())))
|
||||
<div
|
||||
style="
|
||||
{!! $height !== null ? "height: {$height};" : null !!}
|
||||
{!! $width !== null ? "width: {$width};" : null !!}
|
||||
"
|
||||
@class([
|
||||
'flex items-center justify-center bg-gray-100 text-gray-500 ring-white',
|
||||
'dark:bg-gray-600 dark:text-gray-300 dark:ring-gray-800' => config('tables.dark_mode'),
|
||||
'rounded-full' => $isCircular(),
|
||||
$remainingTextSize,
|
||||
$ring,
|
||||
])
|
||||
>
|
||||
<span class="-ml-1">
|
||||
+{{ count($getImages()) - $imageCount }}
|
||||
</span>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
||||
@if ($shouldShowRemaining() && $shouldShowRemainingAfterStack() && ($imageCount < count($getImages())))
|
||||
<div
|
||||
@class([
|
||||
'text-gray-500',
|
||||
'dark:text-gray-300' => config('tables.dark_mode'),
|
||||
$remainingTextSize,
|
||||
])
|
||||
>
|
||||
+{{ count($getImages()) - $imageCount }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
@else
|
||||
<div
|
||||
style="
|
||||
{!! $height !== null ? "height: {$height};" : null !!}
|
||||
{!! $width !== null ? "width: {$width};" : null !!}
|
||||
"
|
||||
@class([
|
||||
'overflow-hidden' => $isCircular() || $isSquare(),
|
||||
'rounded-full' => $isCircular(),
|
||||
])
|
||||
>
|
||||
@if ($path = $getImagePath())
|
||||
<img
|
||||
src="{{ $path }}"
|
||||
style="
|
||||
{!! $height !== null ? "height: {$height};" : null !!}
|
||||
{!! $width !== null ? "width: {$width};" : null !!}
|
||||
"
|
||||
{{ $getExtraImgAttributeBag()->class([
|
||||
'object-cover object-center' => $isCircular() || $isSquare(),
|
||||
]) }}
|
||||
>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@@ -28,6 +28,22 @@ class ImageColumn extends Column
|
||||
|
||||
protected array $extraImgAttributes = [];
|
||||
|
||||
protected bool | Closure $isStacked = false;
|
||||
|
||||
protected string | Closure | null $separator = null;
|
||||
|
||||
protected int | Closure | null $overlap = null;
|
||||
|
||||
protected int | Closure | null $ring = null;
|
||||
|
||||
protected int | Closure | null $limit = null;
|
||||
|
||||
protected bool | Closure $shouldShowRemaining = false;
|
||||
|
||||
protected bool | Closure $shouldShowRemainingAfterStack = false;
|
||||
|
||||
protected string | Closure | null $remainingTextSize = null;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
@@ -118,10 +134,10 @@ class ImageColumn extends Column
|
||||
return $height;
|
||||
}
|
||||
|
||||
public function getImagePath(): ?string
|
||||
public function getImagePath(string | null $image = null): ?string
|
||||
{
|
||||
$state = $this->getState();
|
||||
|
||||
$state = $image ?: $this->getState();
|
||||
|
||||
if (! $state) {
|
||||
return null;
|
||||
}
|
||||
@@ -219,4 +235,123 @@ class ImageColumn extends Column
|
||||
{
|
||||
return new ComponentAttributeBag($this->getExtraImgAttributes());
|
||||
}
|
||||
|
||||
public function stacked(bool | Closure $condition = true): static
|
||||
{
|
||||
$this->isStacked = $condition;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isStacked(): bool
|
||||
{
|
||||
return $this->evaluate($this->isStacked);
|
||||
}
|
||||
|
||||
public function getImages(): array
|
||||
{
|
||||
$images = $this->getState();
|
||||
|
||||
if (is_array($images)) {
|
||||
return $images;
|
||||
}
|
||||
|
||||
if (! ($separator = $this->getSeparator())) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$images = explode($separator, $images);
|
||||
|
||||
if (count($images) === 1 && blank($images[0])) {
|
||||
$images = [];
|
||||
}
|
||||
|
||||
return $images;
|
||||
}
|
||||
|
||||
public function separator(string | Closure | null $separator = ','): static
|
||||
{
|
||||
$this->separator = $separator;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSeparator(): ?string
|
||||
{
|
||||
return $this->evaluate($this->separator);
|
||||
}
|
||||
|
||||
public function overlap(int | Closure | null $overlap): static
|
||||
{
|
||||
$this->overlap = $overlap;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getOverlap(): ?int
|
||||
{
|
||||
return $this->evaluate($this->overlap);
|
||||
}
|
||||
|
||||
public function ring(string | Closure | null $ring): static
|
||||
{
|
||||
$this->ring = $ring;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRing(): ?int
|
||||
{
|
||||
return $this->evaluate($this->ring);
|
||||
}
|
||||
|
||||
public function limit(int | Closure | null $limit = 3): static
|
||||
{
|
||||
$this->limit = $limit;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLimit(): ?int
|
||||
{
|
||||
return $this->evaluate($this->limit);
|
||||
}
|
||||
|
||||
public function showRemaining(bool | Closure $condition = true, bool | Closure $showRemainingAfterStack = false, string | Closure | null $remainingTextSize = null): static
|
||||
{
|
||||
$this->shouldShowRemaining = $condition;
|
||||
$this->showRemainingAfterStack($showRemainingAfterStack);
|
||||
$this->remainingTextSize($remainingTextSize);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function showRemainingAfterStack(bool | Closure $condition = true): static
|
||||
{
|
||||
$this->shouldShowRemainingAfterStack = $condition;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function shouldShowRemaining(): bool
|
||||
{
|
||||
return $this->evaluate($this->shouldShowRemaining);
|
||||
}
|
||||
|
||||
public function shouldShowRemainingAfterStack(): bool
|
||||
{
|
||||
return $this->evaluate($this->shouldShowRemainingAfterStack);
|
||||
}
|
||||
|
||||
public function remainingTextSize(string | Closure | null $remainingTextSize): static
|
||||
{
|
||||
$this->remainingTextSize = $remainingTextSize;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRemainingTextSize(): ?string
|
||||
{
|
||||
return $this->evaluate($this->remainingTextSize);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user