├── .gitignore
├── README.md
├── composer.json
├── dist
├── js
│ └── filter.js
└── mix-manifest.json
├── package.json
├── resources
└── js
│ ├── components
│ ├── Filter.vue
│ └── SelectMultiple.vue
│ └── filter.js
├── screenshot.png
├── src
├── FilterServiceProvider.php
└── MultiselectFilter.php
├── webpack.mix.js
└── yarn.lock
/.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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Nova Multiselect Filter
2 |
3 | Provides capability of selecting multiple values with Nova Resource filter.
4 |
5 | 
6 |
7 | ## Installation
8 |
9 | You can install the package in to a Laravel app that uses [Nova](https://nova.laravel.com) via composer:
10 |
11 | ```bash
12 | composer require rcknr/nova-multiselect-filter
13 | ```
14 |
15 | ## Usage
16 |
17 | Use `MultiselectFilter` class instead of `Filter`:
18 |
19 | ```php
20 | use rcknr\Nova\Filters\MultiselectFilter;
21 |
22 | class UserType extends MultiselectFilter
23 | {
24 | public function apply(Request $request, $query, $value)
25 | {
26 | return $query->whereIn('user_role', $value);
27 | }
28 |
29 | public function options(Request $request)
30 | {
31 | return [
32 | 'Administrator' => 'admin',
33 | 'Editor' => 'editor',
34 | ];
35 | }
36 | }
37 | ```
38 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rcknr/nova-multiselect-filter",
3 | "description": "A Laravel Nova filter that allows multiple selection.",
4 | "keywords": [
5 | "laravel",
6 | "nova"
7 | ],
8 | "license": "MIT",
9 | "authors": [
10 | {
11 | "name": "Sergii Kauk",
12 | "email": "sergii@kauk.at",
13 | "role": "Developer"
14 | }
15 | ],
16 | "require": {
17 | "php": ">=7.1.0"
18 | },
19 | "autoload": {
20 | "psr-4": {
21 | "rcknr\\Nova\\Filters\\": "src/"
22 | }
23 | },
24 | "extra": {
25 | "laravel": {
26 | "providers": [
27 | "rcknr\\Nova\\Filters\\FilterServiceProvider"
28 | ]
29 | }
30 | },
31 | "config": {
32 | "sort-packages": true
33 | },
34 | "minimum-stability": "dev",
35 | "prefer-stable": true
36 | }
37 |
--------------------------------------------------------------------------------
/dist/js/filter.js:
--------------------------------------------------------------------------------
1 | !function(e){var t={};function n(r){if(t[r])return t[r].exports;var s=t[r]={i:r,l:!1,exports:{}};return e[r].call(s.exports,s,s.exports,n),s.l=!0,s.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:r})},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=1)}([function(e,t){e.exports=function(e,t,n,r,s,o){var i,l=e=e||{},a=typeof e.default;"object"!==a&&"function"!==a||(i=e,l=e.default);var c,u="function"==typeof l?l.options:l;if(t&&(u.render=t.render,u.staticRenderFns=t.staticRenderFns,u._compiled=!0),n&&(u.functional=!0),s&&(u._scopeId=s),o?(c=function(e){(e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),r&&r.call(this,e),e&&e._registeredComponents&&e._registeredComponents.add(o)},u._ssrRegister=c):r&&(c=r),c){var d=u.functional,f=d?u.render:u.beforeCreate;d?(u._injectStyles=c,u.render=function(e,t){return c.call(t),f(e,t)}):u.beforeCreate=f?[].concat(f,c):[c]}return{esModule:i,exports:l,options:u}}},function(e,t,n){e.exports=n(2)},function(e,t,n){Nova.booting(function(e){e.component("multiselect-filter",n(3))})},function(e,t,n){var r=n(0)(n(4),n(8),!1,null,null,null);e.exports=r.exports},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(5),s=n.n(r);t.default={components:{SelectMultiple:s.a},props:{resourceName:{type:String,required:!0},filterKey:{type:String,required:!0},lens:String},methods:{handleChange:function(e){this.$store.commit(this.resourceName+"/updateFilterState",{filterClass:this.filterKey,value:e}),this.$emit("change")}},computed:{filter:function(){return this.$store.getters[this.resourceName+"/getFilter"](this.filterKey)},value:function(){return this.filter.currentValue}}}},function(e,t,n){var r=n(0)(n(6),n(7),!1,null,null,null);e.exports=r.exports},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={props:{options:{type:Array,default:[]},value:{default:""}},data:function(){return{showDropdown:!1,selected:[]}},computed:{availableOptions:function(){var e=this;return this.options.filter(function(t){return!e.selected.includes(t)})}},methods:{select:function(e){this.showDropdown=!1,this.selected.push(e)},remove:function(e){this.selected.splice(e,1)},toggle:function(e){[this.$el,this.$refs.selected].includes(e.target)&&this.availableOptions.length>0?this.showDropdown=!this.showDropdown:this.showDropdown=!1}},mounted:function(){var e=this;document.addEventListener("click",this.toggle),this.selected=this.options.filter(function(t){return e.value.includes(t.value)}),console.log(this)},watch:{selected:function(){this.$emit("change",this.selected.map(function(e){return e.value}))}}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"flex h-auto",attrs:{tabindex:"-1"}},[0===e.selected.length?n("div",{staticClass:"h-8 pt-1 leading-normal"},[e._v("—")]):n("ul",{ref:"selected",staticClass:"list-reset flex flex-wrap text-sm -ml-2 pb-1"},e._l(e.selected,function(t,r){return n("li",{staticClass:"bg-primary text-white rounded -ml-0 mt-1 mr-1 px-2 py-1 hover:bg-primary-dark",class:t.value,on:{click:function(t){return e.remove(r)}}},[e._v("\n "+e._s(t.name)+"\n ")])}),0),e._v(" "),e.showDropdown&&e.availableOptions.length?n("ul",{staticClass:"list-reset absolute top-auto w-5/6 -ml-6 py-1 border border-60 rounded-lg bg-30"},e._l(e.availableOptions,function(t){return n("li",{staticClass:"px-3 py-1 hover:text-white hover:bg-primary-dark",on:{click:function(n){return e.select(t)}}},[e._v("\n "+e._s(t.name)+"\n ")])}),0):e._e()])},staticRenderFns:[]}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",[n("h3",{staticClass:"text-sm uppercase tracking-wide text-80 bg-30 p-3"},[e._v("\n "+e._s(e.filter.name)+"\n ")]),e._v(" "),n("div",{staticClass:"p-2"},[n("select-multiple",{staticClass:"block w-full form-control-sm form-select",attrs:{dusk:e.filter.name+"-filter-select",options:e.filter.options,value:e.value},on:{change:e.handleChange}})],1)])},staticRenderFns:[]}}]);
--------------------------------------------------------------------------------
/dist/mix-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "/js/filter.js": "/js/filter.js"
3 | }
--------------------------------------------------------------------------------
/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 | "laravel-nova": "^1.0"
16 | },
17 | "dependencies": {
18 | "vue": "^2.5.0"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/resources/js/components/Filter.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ filter.name }}
5 |
6 |
7 |
8 |
15 |
16 |
17 |
18 |
19 |
20 |
62 |
63 |
--------------------------------------------------------------------------------
/resources/js/components/SelectMultiple.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
—
4 |
5 | -
6 | {{ option.name }}
7 |
8 |
9 |
10 |
11 | -
12 | {{ option.name }}
13 |
14 |
15 |
16 |
17 |
18 |
19 |
70 |
--------------------------------------------------------------------------------
/resources/js/filter.js:
--------------------------------------------------------------------------------
1 | Nova.booting(Vue => {
2 | Vue.component('multiselect-filter', require('./components/Filter'));
3 | })
4 |
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rcknr/nova-multiselect-filter/ac43fe6e9f1339deab00270ad78ffe45fd1807dc/screenshot.png
--------------------------------------------------------------------------------
/src/FilterServiceProvider.php:
--------------------------------------------------------------------------------
1 |