Skip to content

Commit a4f201b

Browse files
authored
Add Nova 4 Support (#30)
1 parent 703271c commit a4f201b

30 files changed

+6351
-763
lines changed

README.md

Lines changed: 56 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -4,41 +4,55 @@
44
[![Total Downloads](https://img.shields.io/packagist/dt/digital-creative/nova-filepond)](https://packagist.org/packages/digital-creative/nova-filepond)
55
[![License](https://img.shields.io/packagist/l/digital-creative/nova-filepond)](https://github.com/dcasia/nova-filepond/blob/master/LICENSE)
66

7-
![Laravel Nova Filepond in action](https://raw.githubusercontent.com/dcasia/nova-filepond/master/screenshots/demo-1.gif)
7+
<picture>
8+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/dcasia/nova-filepond/main/screenshots/dark.png">
9+
<img alt="Laravel Nova Filepond in action" src="https://raw.githubusercontent.com/dcasia/nova-filepond/main/screenshots/light.png">
10+
</picture>
811

912
A Nova field for uploading File, Image and Video using [Filepond](https://github.com/pqina/filepond).
1013

1114
# Installation
1215

1316
You can install the package via composer:
1417

15-
```
18+
```shell
1619
composer require digital-creative/nova-filepond
1720
```
1821

22+
# Features
23+
24+
- Single/Multiple files upload
25+
- Sortable files
26+
- Preview images, videos and audio
27+
- Enable / Disable preview
28+
- Extends the original Laravel Nova File field giving you access to all the methods/functionality of the default file upload.
29+
- Drag and drop files
30+
- Paste files directly from the clipboard
31+
- Store custom attributes (original file name, size, etc)
32+
- Prunable files (Auto delete files when the model is deleted)
33+
- Dark mode support
34+
1935
# Usage
2036

37+
The field extends the original Laravel Nova File field, so you can use all the methods available in the original field.
38+
39+
Basic usage:
40+
2141
```php
2242
use DigitalCreative\Filepond\Filepond;
2343

2444
class Post extends Resource
2545
{
26-
public function fields(Request $request)
46+
public function fields(NovaRequest $request): array
2747
{
2848
return [
29-
// ...
30-
Filepond::make('Audio Example')
31-
->multiple() // the default is single upload, use this method to allow multiple uploads
32-
->limit(4) // limit the number of attached files
33-
->rules('required') // every validation rule works!!
34-
->mimesTypes([ 'audio/mp3', 'audio/ogg', 'audio/vnd.wav' ]) // if opmited, accepts anything
35-
->disk('public', '/optional/location') // the second argument instruct the file to be stored into a subfolder
36-
->storeAs(function (Illuminate\Http\File $file) { // this is optional, use in case you need generate custom file names
37-
return Str::random(20) . '.' . $file->getExtension();
38-
})
39-
49+
Filepond::make('Images', 'images')
50+
->rules('required')
51+
->prunable()
52+
->disablePreview()
53+
->multiple()
54+
->limit(4),
4055
];
41-
4256
}
4357
}
4458
```
@@ -47,68 +61,46 @@ When uploading multiple files you will need to cast the attribute to an array in
4761

4862
```php
4963
class Post extends Model {
50-
51-
/**
52-
* The attributes that should be cast to native types.
53-
*
54-
* @var array
55-
*/
64+
5665
protected $casts = [
5766
'images' => 'array'
5867
];
5968

6069
}
6170
```
6271

63-
64-
# Adding an Image Editor
65-
66-
To enable image editing for your users you have to add the [Doka Image Editor](https://pqina.nl/doka/?ref=nova-filepond).
67-
68-
1. Get a license for the editor and download the Doka library files.
69-
70-
2. Publish the configuration file:
71-
72-
```bash
73-
php artisan vendor:publish --provider="DigitalCreative\Filepond\FilepondServiceProvider" --tag="config"
74-
```
75-
76-
3. Set `doka.enabled` to `true` and update the path to the `doka.min.js` and `doka.min.css` library files.
72+
You can also store original file name / size by using `storeOriginalName` and `storeOriginalSize` methods.
7773

7874
```php
79-
'doka' => [
80-
'enabled' => true,
81-
'js_path' => public_path('doka.min.js'), // this assumes you places theses files within your public directory
82-
'css_path' => public_path('doka.min.css'),
83-
]
84-
```
85-
86-
4. Two options are available to enable/disable Doka on a given field, `->withDoka($options)` accepts a list of options,
87-
you can find the documentation of all possible options here: https://pqina.nl/doka/docs/patterns/api/doka-instance/#properties
75+
use DigitalCreative\Filepond\Filepond;
8876

89-
```php
90-
public function fields(Request $request)
77+
class Post extends Resource
9178
{
92-
return [
93-
//...
94-
95-
Filepond::make('Avatar')->withDoka([
96-
'cropShowSize' => true
97-
]),
98-
99-
/**
100-
* This will disable Doka for this specific field
101-
*/
102-
Filepond::make('Simple Image')->withoutDoka(),
103-
104-
];
79+
public function fields(NovaRequest $request): array
80+
{
81+
return [
82+
Filepond::make('Images', 'images')
83+
->storeOriginalName('name')
84+
->storeSize('size')
85+
->multiple(),
86+
87+
// or you can manually decide how to store the data
88+
// Note: the store method will be called for each file uploaded and the output will be stored into a single json column
89+
Filepond::make('Images', 'images')
90+
->multiple()
91+
->store(function (NovaRequest $request, Model $model, string $attribute): array {
92+
return [
93+
$attribute => $request->images->store('/', 's3'),
94+
'name' => $request->images->getClientOriginalName(),
95+
'size' => $request->images->getSize(),
96+
'metadata' => '...'
97+
];
98+
})
99+
];
100+
}
105101
}
106102
```
107-
108-
If you've setup everything correctly you should see the edit icon on top of FilePond images.
109-
110-
![Laravel Nova Filepond with Doka in action](https://raw.githubusercontent.com/dcasia/nova-filepond/master/screenshots/demo-2.png)
111-
103+
> Note when using `storeOriginalName` and `storeSize` methods, you will need to add the columns to your database table if you are in "single" file mode.
112104
113105
## License
114106

composer.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
"video",
88
"laravel",
99
"nova",
10-
"field",
11-
"doka"
10+
"nova4",
11+
"field"
1212
],
1313
"authors": [
1414
{
@@ -17,7 +17,8 @@
1717
],
1818
"license": "MIT",
1919
"require": {
20-
"php": ">=7.1.0"
20+
"php": ">=8.1",
21+
"laravel/nova": "^4.0"
2122
},
2223
"autoload": {
2324
"psr-4": {

config/nova-filepond.php

Lines changed: 8 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
<?php
22

3+
declare(strict_types = 1);
4+
35
return [
6+
7+
'temp_disk' => 'local',
8+
'temp_path' => 'nova-filepond/temp',
9+
410
/**
5-
* All the values will pass through the trans() function
11+
* All the values will pass through the Nova::__() function
612
*/
713
'labels' => [
814
'decimalSeparator' => 'auto',
@@ -28,56 +34,6 @@
2834
'buttonAbortItemProcessing' => 'Cancel',
2935
'buttonUndoItemProcessing' => 'Undo',
3036
'buttonRetryItemProcessing' => 'Retry',
31-
'buttonProcessItem' => 'Upload'
37+
'buttonProcessItem' => 'Upload',
3238
],
33-
'doka' => [
34-
35-
/**
36-
* Only enable if you have setup doka.min.js correctly, you can find more details how to obtain these files here: https://pqina.nl/doka/?ref=nova-filepond#pricing
37-
* Please use the browser version of doka.min.js.js
38-
*/
39-
'enabled' => false,
40-
'js_path' => public_path('doka.min.js'),
41-
'css_path' => public_path('doka.min.css'),
42-
43-
/**
44-
* Global options, every options contained in here will be merged with every instance of Filepond you create
45-
*/
46-
'options' => [
47-
/**
48-
* Uncomment this to use a circular mask instead of a rectangular one
49-
*/
50-
// 'cropMask' => <<<JAVASCRIPT
51-
// (root, setInnerHTML) => {
52-
// setInnerHTML(root, `
53-
// <mask id="circular-mask">
54-
// <rect x="0" y="0" width="100%" height="100%" fill="white"/>
55-
// <circle cx="50%" cy="50%" r="50%" fill="black"/>
56-
// </mask>
57-
// <rect fill="rgba(255,255,255,.3125)" x="0" y="0" width="100%" height="100%" mask="url(#circular-mask)"/>
58-
// <circle cx="50%" cy="50%" r="50%" fill="transparent" stroke-width="1" stroke="#fff"/>
59-
// `);
60-
// }
61-
// JAVASCRIPT,
62-
'cropShowSize' => true,
63-
'cropAspectRatioOptions' => [
64-
[
65-
'label' => 'Free',
66-
'value' => null
67-
],
68-
[
69-
'label' => 'Portrait',
70-
'value' => 1.25
71-
],
72-
[
73-
'label' => 'Square',
74-
'value' => 1
75-
],
76-
[
77-
'label' => 'Landscape',
78-
'value' => .75
79-
]
80-
]
81-
]
82-
]
8339
];

dist/js/field.js

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/mix-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
22
"/js/field.js": "/js/field.js"
3-
}
3+
}

nova.mix.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const mix = require('laravel-mix')
2+
const webpack = require('webpack')
3+
const path = require('path')
4+
5+
class NovaExtension {
6+
name() {
7+
return 'nova-extension'
8+
}
9+
10+
register(name) {
11+
this.name = name
12+
}
13+
14+
webpackConfig(webpackConfig) {
15+
webpackConfig.externals = {
16+
vue: 'Vue',
17+
}
18+
19+
webpackConfig.resolve.alias = {
20+
...(webpackConfig.resolve.alias || {}),
21+
'laravel-nova': path.join(
22+
__dirname,
23+
'../../vendor/laravel/nova/resources/js/mixins/packages.js'
24+
),
25+
}
26+
27+
webpackConfig.output = {
28+
uniqueName: this.name,
29+
}
30+
}
31+
}
32+
33+
mix.extend('nova', new NovaExtension())

package.json

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,23 @@
11
{
22
"private": true,
33
"scripts": {
4-
"dev": "npm run development",
5-
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
6-
"watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
7-
"watch-poll": "npm run watch -- --watch-poll",
8-
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
9-
"prod": "npm run production",
10-
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
4+
"watch": "mix watch",
5+
"production": "mix --production",
6+
"nova:install": "npm --prefix='../../vendor/laravel/nova' ci"
117
},
128
"devDependencies": {
13-
"cross-env": "^5.0.0",
14-
"laravel-mix": "^1.0",
15-
"laravel-nova": "^1.0"
16-
},
17-
"dependencies": {
18-
"filepond": "^4.7.2",
19-
"filepond-plugin-file-validate-type": "^1.2.4",
20-
"filepond-plugin-image-crop": "^2.0.3",
21-
"filepond-plugin-image-edit": "^1.4.0",
22-
"filepond-plugin-image-exif-orientation": "^1.0.6",
23-
"filepond-plugin-image-overlay": "^1.0.4",
24-
"filepond-plugin-image-preview": "^4.4.0",
25-
"filepond-plugin-image-resize": "^2.0.4",
26-
"filepond-plugin-image-transform": "^3.4.3",
27-
"filepond-plugin-media-preview": "^1.0.3",
28-
"vue": "^2.5.0",
29-
"vue-filepond": "^5.1.3"
9+
"@vue/compiler-sfc": "^3.3.4",
10+
"filepond": "^4.30.4",
11+
"filepond-plugin-file-validate-size": "^2.2.8",
12+
"filepond-plugin-file-validate-type": "^1.2.8",
13+
"filepond-plugin-image-exif-orientation": "^1.0.11",
14+
"filepond-plugin-image-preview": "^4.6.11",
15+
"filepond-plugin-media-preview": "^1.0.11",
16+
"laravel-mix": "^6.0.49",
17+
"postcss": "^8.4.29",
18+
"sass": "^1.67.0",
19+
"sass-loader": "^13.3.2",
20+
"vue-filepond": "^7.0.4",
21+
"vue-loader": "^17.2.2"
3022
}
3123
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<template>
2+
3+
<panel-item :field="field">
4+
5+
<file-pond-wrapper
6+
slot="value"
7+
:disabled="true"
8+
:allow-image-preview="!field.multiple"
9+
:limit="field.value.length"
10+
:field="field"/>
11+
12+
</panel-item>
13+
14+
</template>
15+
16+
<script>
17+
18+
import FilePondWrapper from './FilePondWrapper'
19+
20+
export default {
21+
components: {FilePondWrapper},
22+
props: ['resource', 'resourceName', 'resourceId', 'field']
23+
}
24+
25+
</script>

0 commit comments

Comments
 (0)