Skip to content

Commit

Permalink
Merge pull request #185 from awcodes/feat/multi-select
Browse files Browse the repository at this point in the history
Feat: Add support for multiple images
  • Loading branch information
awcodes authored May 27, 2023
2 parents 0a7c757 + 2eff232 commit d850c1e
Show file tree
Hide file tree
Showing 26 changed files with 728 additions and 283 deletions.
75 changes: 68 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ A media picker/manager plugin for Filament Admin.
> **Warning**
> This package does not work with Spatie Media Library.
> **Warning**
> If you are using the Curator integration with Filament Tiptap Editor you will also need to update it to version 2.3.0 or higher.
![curator-og](https://user-images.githubusercontent.com/3596800/225419661-a0431c1b-957d-466f-a94d-a73a40b11d72.png)

## Installation
Expand Down Expand Up @@ -113,17 +116,50 @@ CuratorPicker::make(string $fieldName)
->imageCropAspectRatio()
->imageResizeTargetWidth()
->imageResizeTargetHeight()
->multiple() // required if using a relationship with multiple media
->relationship(string $relationshipName, string 'titleColumnName')
```

### Relationships

#### Single

Form component

```php
CuratorPicker::make('featured_image_id')
->relationship('featured_image', 'id'),
```

Model

```php
use Awcodes\Curator\Models\Media;

public function featuredImage(): BelongsTo
{
return $this->belongsTo(Media::class, 'featured_image_id', 'id');
}
```

#### Multiple

Form component

```php
CuratorPicker::make('product_picture_ids')
->multiple()
->relationship('product_pictures', 'id'),
```

Media can also be related to models by simply adding the relationship to your
model.
Model

```php
use Awcodes\Curator\Models\Media;

public function featuredImage(): HasOne
public function productPictures(): BelongsTo
{
return $this->hasOne(Media::class, 'id', 'featured_image');
return $this->belongsToMany(Media::class, 'media_post', 'post_id', 'media_id');
}
```

Expand Down Expand Up @@ -175,16 +211,41 @@ CuratorPicker::make(string $fieldName)

### Curator Column

To render your media in a table Curator comes with an `CuratorColumn` which has the same methods as Filament's ImageColumn.
To render your media in a table Curator comes with a `CuratorColumn` which has the same methods as Filament's ImageColumn.

```php
CuratorColumn::make('featured_image')
->size(40)
```

For multiple images you can control the number of images shown, the ring size and the overlap.

```php
CuratorColumn::make('product_pictures')
->ring(2) // options 0,1,2,4
->overlap(4) // options 0,2,3,4
->limit(3),
```

#### Relationships

If you are using a relationship to store your media then you will encounter n+1 issues on the column. In order to prevent this you should modify your table query to eager load the relationship.

For example when using the admin panel in your ListResource
```php
protected function getTableQuery(): Builder
{
return parent::getTableQuery()->with(['featured_image', 'product_pictures']);
}
```

### Curations

Curations are a way to create custom sizes and focal points for your images. After creating curation, they can be referenced by their key to output them in your blade files.
Curations are a way to create custom sizes and focal points for your images.


#### Curation Presets
If you have a curation that you are constantly using you can create Presets which will be available in the Curation modal for easier reuse. After creating curation presets, they can be referenced by their key to output them in your blade files.

```php
use Awcodes\Curator\CurationPreset;
Expand Down Expand Up @@ -357,7 +418,7 @@ public function register()
## Theming

If you are using a custom theme for Filament you will need to add this plugin's
views to your Tailwind CSS config.
views to your `tailwind.config.js`.

```js
content: [
Expand Down
32 changes: 32 additions & 0 deletions resources/css/plugin.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,38 @@
@import '../../node_modules/cropperjs/dist/cropper.css';
@tailwind utilities;

.curator-grid-container {
/**
* User input values.
*/
--grid-layout-gap: theme('spacing.6');
--grid-column-count: 1;
--grid-item--min-width: 75px;

/**
* Calculated values.
*/
--gap-count: calc(var(--grid-column-count) - 1);
--total-gap-width: calc(var(--gap-count) * var(--grid-layout-gap));
--grid-item--max-width: calc((100% - var(--total-gap-width)) / var(--grid-column-count));

display: grid;
grid-template-columns: repeat(auto-fill, minmax(max(var(--grid-item--min-width), var(--grid-item--max-width)), 1fr));
grid-gap: var(--grid-layout-gap);
}

@screen md {
.curator-grid-container {
--grid-column-count: 2;
}
}

@screen lg {
.curator-grid-container {
--grid-column-count: 3;
}
}

.checkered {
background-color: theme("colors.gray.200");
background-image: repeating-linear-gradient(45deg, theme("colors.gray.300") 25%, transparent 25%, transparent 75%, theme("colors.gray.300") 75%, theme("colors.gray.300")), repeating-linear-gradient(45deg, theme("colors.gray.300") 25%, theme("colors.gray.200") 25%, theme("colors.gray.200") 75%, theme("colors.gray.300") 75%, theme("colors.gray.300"));
Expand Down
2 changes: 1 addition & 1 deletion resources/dist/curator.css

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions resources/dist/curator.js

Large diffs are not rendered by default.

77 changes: 52 additions & 25 deletions resources/js/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ document.addEventListener("alpine:init", () => {
}) => ({
statePath,
types,
selected: null,
selected: [],
files: [],
nextPageUrl: null,
isFetching: false,
showEditForm: false,
showUploadForm: true,
async init() {
await this.getFiles('/curator/media', initialSelection?.id);
await this.getFiles('/curator/media', initialSelection);
const observer = new IntersectionObserver(
([e]) => {
if (e.isIntersecting) {
Expand All @@ -26,21 +28,38 @@ document.addEventListener("alpine:init", () => {
}
);
observer.observe(this.$refs.loadMore);
if (initialSelection) {
this.setSelected(initialSelection.id)

this.$watch('selected', (value) => {
if (value.length === 1) {
this.$wire.setSelection(value);
this.showEditForm = true;
this.showUploadForm = false;
} else if (value.length > 1) {
this.showEditForm = false;
this.showUploadForm = false;
} else {
this.showEditForm = false;
this.showUploadForm = true;
}
});

if (initialSelection?.length > 0) {
this.selected = initialSelection;
}
},
getFiles: async function(url = '/curator/media', selected = null) {
if (selected) {
let indicator = url.includes('?') ? '&' : '?';
url = url + indicator + 'media_id=' + selected;
}
this.isFetching = true;
const response = await fetch(url);
const result = await response.json();
this.files = this.files ? this.files.concat(result.data) : result.data;
this.nextPageUrl = result.next_page_url;
this.isFetching = false;
getFiles: async function(url = '/curator/media', selected = []) {
this.$nextTick(async () => {
if (selected.length > 0) {
let indicator = url.includes('?') ? '&' : '?';
url = url + indicator + 'media=' + selected.map(obj => obj.id).join(',');
}
this.isFetching = true;
const response = await fetch(url);
const result = await response.json();
this.files = this.files ? this.files.concat(result.data) : result.data;
this.nextPageUrl = result.next_page_url;
this.isFetching = false;
});
},
loadMoreFiles: async function() {
if (this.nextPageUrl) {
Expand All @@ -60,25 +79,33 @@ document.addEventListener("alpine:init", () => {
if (media) {
this.files = [...media, ...this.files];
this.$nextTick(() => {
this.setSelected(media[0].id);
this.addToSelection(media[0].id);
})
}
},
removeFile: function(media = null) {
if (media) {
this.files = this.files.filter((obj) => obj.id !== media.id);
this.selected = null;
this.removeFromSelection(media.id);
}
},
setSelected: function(mediaId = null) {
if (!mediaId || (this.selected && this.selected.id === mediaId)) {
this.selected = null;
} else {
this.selected = this.files.find(obj => obj.id === mediaId);
}

this.$wire.setCurrentFile(this.selected);
addToSelection: function(mediaId = null) {
this.selected.push(this.files.find(obj => obj.id === mediaId));
},
removeFromSelection: function(mediaId = null) {
this.selected = this.selected.filter((obj) => obj.id !== mediaId);
},
isSelected: function(mediaId = null) {
if (this.selected.length === 0) return false;

return this.selected.find((obj) => obj.id === mediaId) !== undefined;
},
insertMedia: function() {
this.$dispatch('insert-media', {
statePath: this.statePath,
media: this.selected,
});
}
}));

Alpine.data('curation', ({ statePath, fileName, fileType, presets = {}}) => ({
Expand Down
1 change: 1 addition & 0 deletions resources/lang/ar/views.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
],
'picker' => [
'button' => 'إضافة وسائط',
'reorder' => 'إعادة ترتيب',
'view' => 'عرض',
'edit' => 'تعديل',
'download' => 'تنزيل',
Expand Down
1 change: 1 addition & 0 deletions resources/lang/cs/views.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
],
'picker' => [
'button' => 'Přidat média',
'reorder' => 'Přeobjednat',
'view' => 'Zobrazit',
'edit' => 'Upravit',
'download' => 'Stáhnout',
Expand Down
1 change: 1 addition & 0 deletions resources/lang/de/views.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
],
'picker' => [
'button' => 'Datei hinzufügen',
'reorder' => 'Neu anordnen',
'view' => 'Anzeigen',
'edit' => 'Bearbeiten',
'download' => 'Herunterladen',
Expand Down
1 change: 1 addition & 0 deletions resources/lang/en/views.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
],
'picker' => [
'button' => 'Add media',
'reorder' => 'Reorder',
'view' => 'View',
'edit' => 'Edit',
'download' => 'Download',
Expand Down
1 change: 1 addition & 0 deletions resources/lang/fa/views.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
],
'picker' => [
'button' => 'افزودن مدیا',
'reorder' => 'دوباره سفارش دهید',
'view' => 'نمایش',
'edit' => 'ویرایش',
'download' => 'دانلود',
Expand Down
1 change: 1 addition & 0 deletions resources/lang/id/views.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
],
'picker' => [
'button' => 'Tambah media',
'reorder' => 'Susun ulang',
'view' => 'Lihat',
'edit' => 'Ubah',
'download' => 'Unduh',
Expand Down
2 changes: 1 addition & 1 deletion resources/views/components/actions/picker-action.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@
:image-crop-aspect-ratio="$imageCropAspectRatio"
:image-resize-target-width="$imageResizeTargetWidth"
:image-resize-target-height="$imageResizeTargetHeight"
:selected="$selected ?? null"
:selected="$selected ?? []"
/>
2 changes: 1 addition & 1 deletion resources/views/components/curation-input.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
'prefix' => null,
'suffix' => null,
])
<label class="flex items-center w-full border border-gray-300 dark:border-gray-700 rounded-lg shadow-sm bg-gray-400 dark:bg-gray-800 text-sm">
<label class="flex items-center w-full border border-gray-300 dark:border-gray-700 rounded-lg shadow-sm bg-gray-100 dark:bg-gray-800 text-sm">
@if ($prefix)
<span class="w-20 flex-shrink-0 self-stretch flex items-center justify-center px-2">{{ $prefix }}</span>
@endif
Expand Down
4 changes: 2 additions & 2 deletions resources/views/components/curation-select.blade.php
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
@props([
'prefix' => null,
])
<label class="flex items-center w-full border border-gray-300 dark:border-gray-700 rounded-lg shadow-sm bg-gray-400 dark:bg-gray-800 text-sm">
<label class="flex items-center w-full border border-gray-300 dark:border-gray-700 rounded-lg shadow-sm bg-gray-100 dark:bg-gray-800 text-sm">
@if ($prefix)
<span class="w-20 flex-shrink-0 self-stretch flex items-center justify-center px-2">{{ $prefix }}</span>
@endif
<select
@class([
'block w-full !rounded-l-none transition duration-75 focus:border-primary-500 focus:ring-1 focus:ring-inset focus:ring-primary-500 disabled:opacity-70 dark:bg-gray-700 dark:text-white dark:focus:border-primary-500 border border-gray-300 dark:border-gray-700 rounded-lg shadow-sm bg-gray-400 dark:bg-gray-800 text-sm',
'block w-full !rounded-l-none transition duration-75 focus:border-primary-500 focus:ring-1 focus:ring-inset focus:ring-primary-500 disabled:opacity-70 rounded-lg shadow-sm bg-white text-sm border-none dark:text-white dark:focus:border-primary-500 dark:bg-gray-700',
"!rounded-lg" => ! $prefix,
])
{{ $attributes->except(['prefix', 'suffix']) }}
Expand Down
Loading

0 comments on commit d850c1e

Please sign in to comment.