├── dist
├── css
│ └── tool.css
├── mix-manifest.json
└── js
│ └── tool.js
├── webpack.mix.js
├── .gitignore
├── src
├── NovaDynamicViews.php
├── Http
│ ├── Middleware
│ │ └── Authorize.php
│ └── Controllers
│ │ └── NovaDynamicViewsController.php
├── ToolServiceProvider.php
└── CustomComponents.php
├── routes
└── api.php
├── composer.json
├── package.json
├── resources
└── js
│ └── tool.js
└── readme.md
/dist/css/tool.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/mix-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "/js/tool.js": "/js/tool.js"
3 | }
--------------------------------------------------------------------------------
/webpack.mix.js:
--------------------------------------------------------------------------------
1 | let mix = require('laravel-mix')
2 |
3 | mix
4 | .setPublicPath('dist')
5 | .js('resources/js/tool.js', 'js')
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea
2 | /vendor
3 | /node_modules
4 | package-lock.json
5 | composer.phar
6 | composer.lock
7 | phpunit.xml
8 | .phpunit.result.cache
9 | .DS_Store
10 | Thumbs.db
11 |
--------------------------------------------------------------------------------
/src/NovaDynamicViews.php:
--------------------------------------------------------------------------------
1 | =7.1.0"
11 | },
12 | "autoload": {
13 | "psr-4": {
14 | "Bernhardh\\NovaDynamicViews\\": "src/"
15 | }
16 | },
17 | "extra": {
18 | "laravel": {
19 | "providers": [
20 | "Bernhardh\\NovaDynamicViews\\ToolServiceProvider"
21 | ]
22 | }
23 | },
24 | "config": {
25 | "sort-packages": true
26 | },
27 | "minimum-stability": "dev",
28 | "prefer-stable": true
29 | }
30 |
--------------------------------------------------------------------------------
/src/Http/Middleware/Authorize.php:
--------------------------------------------------------------------------------
1 | first([$this, 'matchesTool']);
20 |
21 | return optional($tool)->authorize($request) ? $next($request) : abort(403);
22 | }
23 |
24 | /**
25 | * Determine whether this tool belongs to the package.
26 | *
27 | * @param \Laravel\Nova\Tool $tool
28 | * @return bool
29 | */
30 | public function matchesTool($tool)
31 | {
32 | return $tool instanceof NovaDynamicViews;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "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"
11 | },
12 | "devDependencies": {
13 | "cross-env": "^5.0.0",
14 | "laravel-mix": "^1.0"
15 | },
16 | "dependencies": {
17 | "vue": "^2.5.0"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Http/Controllers/NovaDynamicViewsController.php:
--------------------------------------------------------------------------------
1 | resource();
22 | $model = $request->model();
23 | $method = Str::camel('custom-' . $method . '-components');
24 | $resource = new $resourceClass($model);
25 |
26 | if(method_exists($resource, $method)) {
27 | $data = $resource->$method();
28 | if($data) {
29 | return $data;
30 | }
31 | }
32 |
33 | return [];
34 | }
35 | }
--------------------------------------------------------------------------------
/src/ToolServiceProvider.php:
--------------------------------------------------------------------------------
1 | app->booted(function () {
21 | $this->routes();
22 | });
23 | }
24 |
25 | /**
26 | * Register the tool's routes.
27 | *
28 | * @return void
29 | */
30 | protected function routes()
31 | {
32 | if ($this->app->routesAreCached()) {
33 | return;
34 | }
35 |
36 | Route::middleware(['nova', Authorize::class])
37 | ->prefix('nova-vendor/nova-dynamic-views')
38 | ->group(__DIR__.'/../routes/api.php');
39 | }
40 |
41 | /**
42 | * Register any application services.
43 | *
44 | * @return void
45 | */
46 | public function register()
47 | {
48 | //
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/dist/js/tool.js:
--------------------------------------------------------------------------------
1 | !function(e){var t={};function o(n){if(t[n])return t[n].exports;var a=t[n]={i:n,l:!1,exports:{}};return e[n].call(a.exports,a,a.exports,o),a.l=!0,a.exports}o.m=e,o.c=t,o.d=function(e,t,n){o.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:n})},o.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(t,"a",t),t},o.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},o.p="",o(o.s=0)}([function(e,t,o){e.exports=o(1)},function(e,t){Nova.booting(function(e){["attach-header","create-header","detail-header","detail-toolbar","index-header","index-toolbar","lens-header","update-attach-header","update-header"].forEach(function(t){e.component("custom-"+t,{props:["resourceName"],template:'
',data:function(){return{customComponents:[],compClass:"",compName:t}},mounted:function(){var e=this,t="/nova-vendor/nova-dynamic-views/"+this.resourceName+"/"+this.compName;this.$route.params&&this.$route.params.resourceId&&(t+="?id="+this.$route.params.resourceId),Nova.request().get(t).then(function(t){var o=t.data.items||[];if(o)for(var n in o)for(var a in e.$props)o[n].meta||(o[n].meta={}),o[n].meta[a]=e[a];e.customComponents=o,e.compClass=t.data.class})}})})})}]);
--------------------------------------------------------------------------------
/src/CustomComponents.php:
--------------------------------------------------------------------------------
1 | class = $class;
26 |
27 | return $obj;
28 | }
29 |
30 |
31 | /**
32 | * @param string $name
33 | * @param array $meta
34 | *
35 | * @return $this
36 | */
37 | public function addItem($name, $meta = [])
38 | {
39 | $item = ['name'=> $name];
40 | if($meta) {
41 | $item['meta'] = $meta;
42 | }
43 |
44 | $this->items[] = $item;
45 |
46 | return $this;
47 | }
48 |
49 |
50 | /**
51 | * @param string $class
52 | *
53 | * @return CustomComponents
54 | */
55 | public function setClass(string $class) {
56 | $this->class = $class;
57 |
58 | return $this;
59 | }
60 |
61 |
62 | /**
63 | * @return array|mixed
64 | */
65 | public function jsonSerialize()
66 | {
67 | return array_filter([
68 | 'class' => $this->class,
69 | 'items' => $this->items
70 | ]);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/resources/js/tool.js:
--------------------------------------------------------------------------------
1 | Nova.booting((Vue) => {
2 | const components = [
3 | 'attach-header',
4 | 'create-header',
5 | 'detail-header',
6 | 'detail-toolbar',
7 | 'index-header',
8 | 'index-toolbar',
9 | 'lens-header',
10 | 'update-attach-header',
11 | 'update-header'
12 | ];
13 |
14 | components.forEach((comp) => {
15 | Vue.component('custom-' + comp, {
16 | props: ['resourceName'],
17 |
18 | template: '' +
19 | '' +
20 | '
',
21 |
22 | data() {
23 | return {
24 | customComponents: [],
25 | compClass: '',
26 | compName: comp
27 | }
28 | },
29 |
30 | mounted() {
31 | let url = '/nova-vendor/nova-dynamic-views/' + this.resourceName + '/' + this.compName;
32 | if(this.$route.params && this.$route.params.resourceId) {
33 | url+= '?id=' + this.$route.params.resourceId
34 | }
35 |
36 | Nova.request().get(url)
37 | .then(res => {
38 | let items = res.data.items || []
39 | if(items) {
40 | for(let i in items) {
41 | for(let j in this.$props) {
42 | if(!items[i].meta) items[i].meta = {}
43 | items[i].meta[j] = this[j]
44 | }
45 | }
46 | }
47 | this.customComponents = items
48 | this.compClass = res.data.class
49 | })
50 | }
51 | })
52 | })
53 | })
54 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Warning!!!!
2 |
3 | This repository is archived and no longer supported. It doesn't work with Nova 4. Just have a look at the form from https://github.com/shuvroroy/nova-dynamic-views
4 |
5 |
6 | ---
7 |
8 |
9 | # Nova dynamic views
10 |
11 | This package is meant to be used **INSTEAD** of overwriting the `custom-index-header`, `custom-index-toolbar`, `custom-detail-header`, `custom-detail-toolbar`, etc. by yourself. It provides a much easier API for it and it allows you to use these "placeholder" components multiple times without overwriting each other.
12 |
13 | 
14 |
15 | ## Installation
16 |
17 | Require the package with composer
18 |
19 | ```
20 | composer require bernhardh/nova-dynamic-views
21 | ```
22 |
23 | Register the tool in the `tools` method in your `\App\Providers\NovaServiceProvider`:
24 |
25 | ```php
26 | use Bernhardh\NovaDynamicViews\NovaDynamicViews;
27 |
28 | ...
29 |
30 | public function tools() {
31 | return [
32 | new NovaDynamicViews()
33 | ];
34 | }
35 | ```
36 |
37 | ## Usage
38 |
39 | Let's say you want to add a custom button to the `toolbar` of all `index` views. Just create a vue component for it, as you would do if you use the `custom-index-header` (see section "Create custom component" if you don't know how to). Let's call it `my-index-toolbar-btn`. Now the only thing you have to do is register it to your `\App\Ņova\Resource` class, within a new method called `customIndexToolbarComponents`, which returns a `\Bernhardh\NovaDynamicViews\CustomComponents` object:
40 |
41 | ```php
42 | public function customIndexToolbarComponents()
43 | {
44 | return CustomComponents::make()
45 | ->addItem('my-index-toolbar-btn');
46 | }
47 | ```
48 |
49 | Thats it. Now you should see the content of your component in the toolbar.
50 |
51 | ### Provide extra data
52 |
53 | If you want to add extra data (for example a label) to your component (without extra request), just add it to the `addItem` method as second parameter (as array):
54 |
55 | ```php
56 | public function customIndexToolbarComponents()
57 | {
58 | return CustomComponents::make()
59 | ->addItem('my-index-toolbar-btn', [
60 | 'label' => 'My label'
61 | ]);
62 | }
63 | ```
64 |
65 | ### Access resource data
66 |
67 | You have access to the ressource class in all methods by using `$this`. On `detail` and `edit` components, you have access to the ID of the current model with `request('id')`. So if you need the model itself in your `customDetailhHeaderComponents`, `customDetailToolbarComponents` or your `customUpdateHeaderComponents`, you can query for it like so:
68 |
69 | ```php
70 | public function customDetailToolbarComponents()
71 | {
72 | $model = $this->model()->query()->where('id', request('id'))->first();
73 |
74 | //...
75 | }
76 | ```
77 |
78 | ### Add (tailwind) class to the container
79 |
80 | If you want to add additional CSS classes to the container div of a section (for example add `flex w-full justify-end items-center mx-3` to the `customIndexToolbarComponents` section), add the `class` in the `make` function (or use the `setClass` method):
81 |
82 | ```php
83 | public function customIndexToolbarComponents()
84 | {
85 | return CustomComponents::make('flex w-full justify-end items-center mx-3')
86 | ->addItem('my-index-toolbar-btn');
87 | }
88 | ```
89 |
90 | ### Full usage example
91 |
92 | ```php
93 | class Resource extends \Laravel\Nova\Resource {
94 | ...
95 |
96 | /**
97 | * Using the `custom-index-toolbar` placeholder component
98 | *
99 | * @return array[]
100 | */
101 | public function customIndexToolbarComponents()
102 | {
103 | return CustomComponents::make('flex w-full justify-end items-center mx-3')
104 | ->addItem('my-index-toolbar-btn', [
105 | 'title' => 'My first btn'
106 | ])
107 | ->addItem('my-index-toolbar-btn', [
108 | 'title' => 'My second btn'
109 | ]);
110 | }
111 |
112 | /**
113 | * Using the `custom-detail-header` placeholder component
114 | *
115 | * @return array[]
116 | */
117 | public function customDetailHeaderComponents()
118 | {
119 | $model = $this->model()->query()->where('id', request('id'))->first();
120 |
121 | return CustomComponents::make()
122 | ->addItem('my-other-component', [
123 | 'id' => $model->id,
124 | 'name' => $model->name
125 | ]);
126 | }
127 | }
128 | ```
129 |
130 |
131 | ### Use only on specific resources
132 |
133 | If you want to show this button only on a specific resource, for example only for Users, just add this method to the `\App\Nova\User` class.
134 |
135 | ## Available methods and areas
136 |
137 | All `custom-*-*` nova placeholders (except `custom-dashboard-header`) are available as camel case methods postfixed with `Components`:
138 |
139 | - `customAttachHeaderComponents`
140 | - `customCreateHeaderComponents`
141 | - `customDetailhHeaderComponents`
142 | - `customDetailToolbarComponents`
143 | - `customIndexHeaderComponents`
144 | - `customIndexToolbarComponents`
145 | - `customLensHeaderComponents`
146 | - `customUpdateAttachHeaderComponents`
147 | - `customUpdateHeaderComponents`
148 |
149 | ## Create custom component
150 |
151 | This is just a kick start documentation for this. For more info, see https://nova.laravel.com/docs/3.0/customization/resource-tools.html
152 |
153 | Create a new resource tool with artisan:
154 |
155 | ```bash
156 | php artisan nova:resource-tool acme/my-index-toolbar-btn
157 | ```
158 |
159 | and say yes to all questions of the prompt. Now you can use this component (located ad `nova-components/my-index-toolbar-btn`) inside your `customXXXComponents` (f.e. `customIndexToolbarComponents`)
160 |
161 |
162 |
--------------------------------------------------------------------------------