├── .gitignore ├── bun.lockb ├── changelog.md ├── composer.json ├── dist ├── js │ └── filter.js └── mix-manifest.json ├── license.md ├── mix.js ├── package-lock.json ├── package.json ├── readme.md ├── readme └── img │ ├── multiselect.png │ └── typing.png ├── resources └── js │ ├── components │ └── ComboboxFilter.vue │ └── filter.js ├── src ├── FilterServiceProvider.php └── NovaComboboxFilter.php └── webpack.mix.js /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /vendor 3 | /node_modules 4 | composer.phar 5 | composer.lock 6 | phpunit.xml 7 | .phpunit.result.cache 8 | .DS_Store 9 | Thumbs.db 10 | -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Harrald/nova-combobox-filter/2fac8af7c79ef231ce81e853676a9527364c2490/bun.lockb -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [1.2.0] - 2024-12-18 9 | 10 | ### Added 11 | - Support Nova 5 12 | 13 | ## [1.1.0] - 2024-02-20 14 | 15 | ### Changed 16 | - added a button next to the list label which toggles between a wide/narrow list 17 | 18 | ## [1.0.8] - 2023-08-31 19 | 20 | ### Changed 21 | - added Collection return type to NovaComboboxFilter::options method 22 | 23 | ## [1.0.7] - 2023-03-15 24 | 25 | ### Fixed 26 | - resetting all the nova filters broke this component 27 | 28 | ### Changed 29 | - switched from yarn to npm 30 | 31 | ## [1.0.6] - 2022-05-09 32 | 33 | ### Changed 34 | - Changed readme img urls from relative to absolute 35 | 36 | ## [1.0.5] - 2022-05-09 37 | 38 | ### Changed 39 | - Require `laravel/nova` from `require` instead of `require-dev` 40 | 41 | ## [1.0.4] - 2022-04-29 42 | 43 | ### Changed 44 | - Use headlessui 1.6 instead of the insides build 45 | - Improved usage of filter and improved readme.md 46 | 47 | ## [1.0.3] - 2022-04-29 48 | 49 | ### Fixed 50 | - Make Nova 'reset filters' work 51 | - Use correct 'null' value for empty state (filters were incorrectly being displayed als having a selected value) 52 | 53 | ## [1.0.2] - 2022-04-11 54 | 55 | ### Added 56 | - Add `license.md` 57 | - Add this `changelog.md` 58 | - Add packagist shields to `readme.md` 59 | - Denote Laravel Nova dependency in `composer.json` 60 | 61 | ### Removed 62 | - Removed unused code 63 | 64 | ## [1.0.1] - 2022-04-11 65 | 66 | ### Changed 67 | - Make checkmark svg different color then background 68 | 69 | ## [1.0.0] - 2022-04-11 70 | 71 | Initial release. 72 | Combobox/multiselect filter for Nova 4. Based on [Headless UI Combobox component](https://headlessui.dev/vue/combobox) 73 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "harrald/nova-combobox-filter", 3 | "description": "A Laravel Nova combobox filter. Supports selecting multiple items", 4 | "keywords": [ 5 | "laravel", 6 | "nova" 7 | ], 8 | "license": "MIT", 9 | "require": { 10 | "php": "^7.3|^8.0", 11 | "laravel/nova": "^4.0|^5.0" 12 | }, 13 | "autoload": { 14 | "psr-4": { 15 | "Harrald\\NovaComboboxFilter\\": "src/" 16 | } 17 | }, 18 | "extra": { 19 | "laravel": { 20 | "providers": [ 21 | "Harrald\\NovaComboboxFilter\\FilterServiceProvider" 22 | ] 23 | } 24 | }, 25 | "config": { 26 | "sort-packages": true 27 | }, 28 | "minimum-stability": "dev", 29 | "prefer-stable": true, 30 | "repositories": { 31 | "nova": { 32 | "type": "composer", 33 | "url": "https://nova.laravel.com" 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /dist/js/filter.js: -------------------------------------------------------------------------------- 1 | (()=>{"use strict";var e={739:(e,t,o)=>{o.d(t,{Z:()=>r});var n=o(645),a=o.n(n)()((function(e){return e[1]}));a.push([e.id,".combobox-input-container[data-v-4ef21301]{border-color:rgb(var(--colors-gray-300));border-radius:.25rem;border-width:1px;min-height:2rem;overflow:hidden;position:relative}.dark .combobox-input-container[data-v-4ef21301]{--tw-ring-color:rgb(var(--colors-gray-700));border-color:rgb(var(--colors-gray-700))}.combobox-input-container[data-v-4ef21301]:focus-within{border-color:rgba(var(--colors-primary-300));box-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color),var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color),var(--tw-shadow,0 0 transparent);outline:2px solid transparent;outline-offset:2px}.dark .combobox-input-container[data-v-4ef21301]:focus-within{border-color:rgb(var(--colors-gray-400))}.combobox-input[data-v-4ef21301]{background-color:#fff;border:0;color:rgba(var(--colors-gray-900));font-size:.875rem;height:2rem;line-height:1.25rem;padding-bottom:.5rem;padding-left:1rem;padding-top:.5rem;width:100%}.dark .combobox-input[data-v-4ef21301]{background-color:rgb(var(--colors-gray-900));color:rgb(var(--colors-gray-400))}.combobox-input[data-v-4ef21301]:focus{outline:none}.combobox-input--has-selected-options[data-v-4ef21301]{border-top:1px solid rgb(var(--colors-gray-300));margin-top:5px}.dark .combobox-input--has-selected-options[data-v-4ef21301]{border-color:rgb(var(--colors-gray-700))}.combobox-button[data-v-4ef21301]{align-items:flex-end;display:flex;height:100%;justify-content:center;padding-bottom:13px;position:absolute;right:0;top:0;width:22px}.selected-option-button-list[data-v-4ef21301]{padding-left:5px}.selected-option-button[data-v-4ef21301]{background-color:rgba(var(--colors-primary-500));border-radius:4px;color:#fff;height:22px;margin:5px 5px 0 0;padding:1px 25px 1px 5px;position:relative}.selected-option-button[data-v-4ef21301]>:last-child{aspect-ratio:1/1;height:100%;padding:3px;position:absolute;right:0;top:0;transition:background-color .2s}.selected-option-button>:last-child>*[data-v-4ef21301]{fill:rgba(0,0,0,.3)}.selected-option-button[data-v-4ef21301]:hover>:last-child{background-color:rgba(var(--colors-primary-600))}.selected-option-button:hover>:last-child>*[data-v-4ef21301]{fill:#fff}.combobox-options[data-v-4ef21301]{--combobox-options-boxshadow-base-color:0,0,0;background-color:#fff;border-radius:.25rem;box-shadow:0 10px 15px -3px rgba(var(--combobox-options-boxshadow-base-color),.1),0 4px 6px -2px rgba(var(--combobox-options-boxshadow-base-color),.05);color:#111827;margin-top:.25rem;max-height:200px;overflow:auto;padding-bottom:.25rem;padding-top:.25rem;position:absolute;width:100%;z-index:1}.dark .combobox-options[data-v-4ef21301]{--combobox-options-boxshadow-base-color:255,255,255;background-color:rgb(var(--colors-gray-900));color:rgb(var(--colors-gray-400))}.wide-narrow-btn[data-v-4ef21301]{position:relative;top:2px}.combobox-option[data-v-4ef21301]{color:inherit;cursor:pointer;font-size:14px;line-height:16px;overflow:hidden;padding:.5rem 1rem .5rem 2.5rem;position:relative;text-overflow:ellipsis;-webkit-user-select:none;-moz-user-select:none;user-select:none}.combobox-option--is-selected[data-v-4ef21301]{font-weight:700}.combobox-option--is-active[data-v-4ef21301],.combobox-option[data-v-4ef21301]:hover{background-color:rgba(var(--colors-primary-500));color:#fff}.combobox-option__label[data-v-4ef21301]{white-space:nowrap}.combobox-option__label--wide[data-v-4ef21301]{word-break:break-all}.combobox-option__icon[data-v-4ef21301]{align-items:center;bottom:0;display:none;left:0;padding-left:.75rem;position:absolute;top:0}.combobox-option__icon>*[data-v-4ef21301]{aspect-ratio:1/1;width:1.25rem}.combobox-option__icon--is-selected[data-v-4ef21301]{display:flex}.combobox-option__icon--is-selected>*[data-v-4ef21301]{fill:rgba(var(--colors-primary-500))}.combobox-option:hover .combobox-option__icon--is-selected>*[data-v-4ef21301]{fill:currentColor}",""]);const r=a},645:e=>{e.exports=function(e){var t=[];return t.toString=function(){return this.map((function(t){var o=e(t);return t[2]?"@media ".concat(t[2]," {").concat(o,"}"):o})).join("")},t.i=function(e,o,n){"string"==typeof e&&(e=[[null,e,""]]);var a={};if(n)for(var r=0;r{var n,a=function(){return void 0===n&&(n=Boolean(window&&document&&document.all&&!window.atob)),n},r=function(){var e={};return function(t){if(void 0===e[t]){var o=document.querySelector(t);if(window.HTMLIFrameElement&&o instanceof window.HTMLIFrameElement)try{o=o.contentDocument.head}catch(e){o=null}e[t]=o}return e[t]}}(),l=[];function i(e){for(var t=-1,o=0;o{t.Z=(e,t)=>{const o=e.__vccOpts||e;for(const[e,n]of t)o[e]=n;return o}}},t={};function o(n){var a=t[n];if(void 0!==a)return a.exports;var r=t[n]={id:n,exports:{}};return e[n](r,r.exports,o),r.exports}o.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return o.d(t,{a:t}),t},o.d=(e,t)=>{for(var n in t)o.o(t,n)&&!o.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},o.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),o.nc=void 0,(()=>{const e=Vue;function t(e,o,...n){if(e in o){let t=o[e];return"function"==typeof t?t(...n):t}let a=new Error(`Tried to handle "${e}" but there is no handler defined. Only defined handlers are: ${Object.keys(o).map((e=>`"${e}"`)).join(", ")}.`);throw Error.captureStackTrace&&Error.captureStackTrace(a,t),a}var n,a=((n=a||{})[n.None=0]="None",n[n.RenderStrategy=1]="RenderStrategy",n[n.Static=2]="Static",n),r=(e=>(e[e.Unmount=0]="Unmount",e[e.Hidden=1]="Hidden",e))(r||{});function l({visible:e=!0,features:o=0,ourProps:n,theirProps:a,...r}){var l;let u=s(a,n),c=Object.assign(r,{props:u});if(e||2&o&&u.static)return i(c);if(1&o){return t(null==(l=u.unmount)||l?0:1,{0:()=>null,1:()=>i({...r,props:{...u,hidden:!0,style:{display:"none"}}})})}return i(c)}function i({props:t,attrs:o,slots:n,slot:a,name:r}){var l,i;let{as:d,...p}=c(t,["unmount","static"]),v=null==(l=n.default)?void 0:l.call(n,a),f={};if(a){let e=!1,t=[];for(let[o,n]of Object.entries(a))"boolean"==typeof n&&(e=!0),!0===n&&t.push(o);e&&(f["data-headlessui-state"]=t.join(" "))}if("template"===d){if(v=u(null!=v?v:[]),Object.keys(p).length>0||Object.keys(o).length>0){let[t,...n]=null!=v?v:[];if(!function(e){return null!=e&&("string"==typeof e.type||"object"==typeof e.type||"function"==typeof e.type)}(t)||n.length>0)throw new Error(['Passing props on "template"!',"",`The current component <${r} /> is rendering a "template".`,"However we need to passthrough the following props:",Object.keys(p).concat(Object.keys(o)).map((e=>e.trim())).filter(((e,t,o)=>o.indexOf(e)===t)).sort(((e,t)=>e.localeCompare(t))).map((e=>` - ${e}`)).join("\n"),"","You can apply a few solutions:",['Add an `as="..."` prop, to ensure that we render an actual element instead of a "template".',"Render a single element as the child so that we can forward the props onto that element."].map((e=>` - ${e}`)).join("\n")].join("\n"));let a=s(null!=(i=t.props)?i:{},p),l=(0,e.cloneVNode)(t,a);for(let e in a)e.startsWith("on")&&(l.props||(l.props={}),l.props[e]=a[e]);return l}return Array.isArray(v)&&1===v.length?v[0]:v}return(0,e.h)(d,Object.assign({},p,f),{default:()=>v})}function u(t){return t.flatMap((t=>t.type===e.Fragment?u(t.children):[t]))}function s(...e){if(0===e.length)return{};if(1===e.length)return e[0];let t={},o={};for(let n of e)for(let e in n)e.startsWith("on")&&"function"==typeof n[e]?(null!=o[e]||(o[e]=[]),o[e].push(n[e])):t[e]=n[e];if(t.disabled||t["aria-disabled"])return Object.assign(t,Object.fromEntries(Object.keys(o).map((e=>[e,void 0]))));for(let e in o)Object.assign(t,{[e](t,...n){let a=o[e];for(let e of a){if(t instanceof Event&&t.defaultPrevented)return;e(t,...n)}}});return t}function c(e,t=[]){let o=Object.assign({},e);for(let e of t)e in o&&delete o[e];return o}let d=0;function p(){return++d}var v=(e=>(e.Space=" ",e.Enter="Enter",e.Escape="Escape",e.Backspace="Backspace",e.Delete="Delete",e.ArrowLeft="ArrowLeft",e.ArrowUp="ArrowUp",e.ArrowRight="ArrowRight",e.ArrowDown="ArrowDown",e.Home="Home",e.End="End",e.PageUp="PageUp",e.PageDown="PageDown",e.Tab="Tab",e))(v||{});var f=(e=>(e[e.First=0]="First",e[e.Previous=1]="Previous",e[e.Next=2]="Next",e[e.Last=3]="Last",e[e.Specific=4]="Specific",e[e.Nothing=5]="Nothing",e))(f||{});function b(e,t){let o=t.resolveItems();if(o.length<=0)return null;let n=t.resolveActiveIndex(),a=null!=n?n:-1,r=(()=>{switch(e.focus){case 0:return o.findIndex((e=>!t.resolveDisabled(e)));case 1:{let e=o.slice().reverse().findIndex(((e,o,n)=>!(-1!==a&&n.length-o-1>=a)&&!t.resolveDisabled(e)));return-1===e?e:o.length-1-e}case 2:return o.findIndex(((e,o)=>!(o<=a)&&!t.resolveDisabled(e)));case 3:{let e=o.slice().reverse().findIndex((e=>!t.resolveDisabled(e)));return-1===e?e:o.length-1-e}case 4:return o.findIndex((o=>t.resolveId(o)===e.id));case 5:return null;default:!function(e){throw new Error("Unexpected object: "+e)}(e)}})();return-1===r?n:r}function m(e){var t;return null==e||null==e.value?null:null!=(t=e.value.$el)?t:e.value}let g=Symbol("Context");var h=(e=>(e[e.Open=1]="Open",e[e.Closed=2]="Closed",e[e.Closing=4]="Closing",e[e.Opening=8]="Opening",e))(h||{});function x(){return(0,e.inject)(g,null)}function w(e,t){if(e)return e;let o=null!=t?t:"button";return"string"==typeof o&&"button"===o.toLowerCase()?"button":void 0}var y=Object.defineProperty,O=(e,t,o)=>(((e,t,o)=>{t in e?y(e,t,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[t]=o})(e,"symbol"!=typeof t?t+"":t,o),o);let S=new class{constructor(){O(this,"current",this.detect()),O(this,"currentId",0)}set(e){this.current!==e&&(this.currentId=0,this.current=e)}reset(){this.set(this.detect())}nextId(){return++this.currentId}get isServer(){return"server"===this.current}get isClient(){return"client"===this.current}detect(){return"undefined"==typeof window||"undefined"==typeof document?"server":"client"}};function C(e){if(S.isServer)return null;if(e instanceof Node)return e.ownerDocument;if(null!=e&&e.hasOwnProperty("value")){let t=m(e);if(t)return t.ownerDocument}return document}let R=["[contentEditable=true]","[tabindex]","a[href]","area[href]","button:not([disabled])","iframe","input:not([disabled])","select:not([disabled])","textarea:not([disabled])"].map((e=>`${e}:not([tabindex='-1'])`)).join(",");var k=(e=>(e[e.First=1]="First",e[e.Previous=2]="Previous",e[e.Next=4]="Next",e[e.Last=8]="Last",e[e.WrapAround=16]="WrapAround",e[e.NoScroll=32]="NoScroll",e))(k||{}),E=(e=>(e[e.Error=0]="Error",e[e.Overflow=1]="Overflow",e[e.Success=2]="Success",e[e.Underflow=3]="Underflow",e))(E||{}),N=(e=>(e[e.Previous=-1]="Previous",e[e.Next=1]="Next",e))(N||{});var I=(e=>(e[e.Strict=0]="Strict",e[e.Loose=1]="Loose",e))(I||{});function P(e,o=0){var n;return e!==(null==(n=C(e))?void 0:n.body)&&t(o,{0:()=>e.matches(R),1(){let t=e;for(;null!==t;){if(t.matches(R))return!0;t=t.parentElement}return!1}})}["textarea","input"].join(",");function V(e,t=(e=>e)){return e.slice().sort(((e,o)=>{let n=t(e),a=t(o);if(null===n||null===a)return 0;let r=n.compareDocumentPosition(a);return r&Node.DOCUMENT_POSITION_FOLLOWING?-1:r&Node.DOCUMENT_POSITION_PRECEDING?1:0}))}function T(t,o,n){S.isServer||(0,e.watchEffect)((e=>{document.addEventListener(t,o,n),e((()=>document.removeEventListener(t,o,n)))}))}function j(t,o,n=(0,e.computed)((()=>!0))){function a(e,a){if(!n.value||e.defaultPrevented)return;let r=a(e);if(null===r||!r.getRootNode().contains(r))return;let l=function e(t){return"function"==typeof t?e(t()):Array.isArray(t)||t instanceof Set?t:[t]}(t);for(let t of l){if(null===t)continue;let o=t instanceof HTMLElement?t:m(t);if(null!=o&&o.contains(r)||e.composed&&e.composedPath().includes(o))return}return!P(r,I.Loose)&&-1!==r.tabIndex&&e.preventDefault(),o(e,r)}let r=(0,e.ref)(null);T("mousedown",(e=>{var t,o;n.value&&(r.value=(null==(o=null==(t=e.composedPath)?void 0:t.call(e))?void 0:o[0])||e.target)}),!0),T("click",(e=>{r.value&&(a(e,(()=>r.value)),r.value=null)}),!0),T("blur",(e=>a(e,(()=>window.document.activeElement instanceof HTMLIFrameElement?window.document.activeElement:null))),!0)}var L=(e=>(e[e.None=1]="None",e[e.Focusable=2]="Focusable",e[e.Hidden=4]="Hidden",e))(L||{});let M=(0,e.defineComponent)({name:"Hidden",props:{as:{type:[Object,String],default:"div"},features:{type:Number,default:1}},setup:(e,{slots:t,attrs:o})=>()=>{let{features:n,...a}=e;return l({ourProps:{"aria-hidden":2==(2&n)||void 0,style:{position:"fixed",top:1,left:1,width:1,height:0,padding:0,margin:-1,overflow:"hidden",clip:"rect(0, 0, 0, 0)",whiteSpace:"nowrap",borderWidth:"0",...4==(4&n)&&2!=(2&n)&&{display:"none"}}},theirProps:a,slot:{},attrs:o,slots:t,name:"Hidden"})}});function _(e={},t=null,o=[]){for(let[n,a]of Object.entries(e))B(o,A(t,n),a);return o}function A(e,t){return e?e+"["+t+"]":t}function B(e,t,o){if(Array.isArray(o))for(let[n,a]of o.entries())B(e,A(t,n.toString()),a);else o instanceof Date?e.push([t,o.toISOString()]):"boolean"==typeof o?e.push([t,o?"1":"0"]):"string"==typeof o?e.push([t,o]):"number"==typeof o?e.push([t,`${o}`]):null==o?e.push([t,""]):_(o,t,e)}function D(e){return[e.screenX,e.screenY]}function F(){return/iPhone/gi.test(window.navigator.platform)||/Mac/gi.test(window.navigator.platform)&&window.navigator.maxTouchPoints>0||/Android/gi.test(window.navigator.userAgent)}function $(e,t){return e===t}var U=(e=>(e[e.Open=0]="Open",e[e.Closed=1]="Closed",e))(U||{}),H=(e=>(e[e.Single=0]="Single",e[e.Multi=1]="Multi",e))(H||{}),z=(e=>(e[e.Pointer=0]="Pointer",e[e.Other=1]="Other",e))(z||{});let W=Symbol("ComboboxContext");function K(t){let o=(0,e.inject)(W,null);if(null===o){let e=new Error(`<${t} /> is missing a parent component.`);throw Error.captureStackTrace&&Error.captureStackTrace(e,K),e}return o}let q=(0,e.defineComponent)({name:"Combobox",emits:{"update:modelValue":e=>!0},props:{as:{type:[Object,String],default:"template"},disabled:{type:[Boolean],default:!1},by:{type:[String,Function],default:()=>$},modelValue:{type:[Object,String,Number,Boolean],default:void 0},defaultValue:{type:[Object,String,Number,Boolean],default:void 0},name:{type:String},nullable:{type:Boolean,default:!1},multiple:{type:[Boolean],default:!1}},inheritAttrs:!1,setup(o,{slots:n,attrs:a,emit:r}){let i=(0,e.ref)(1),u=(0,e.ref)(null),s=(0,e.ref)(null),d=(0,e.ref)(null),p=(0,e.ref)(null),v=(0,e.ref)({static:!1,hold:!1}),x=(0,e.ref)([]),w=(0,e.ref)(null),y=(0,e.ref)(1),O=(0,e.ref)(!1);function S(e=(e=>e)){let t=null!==w.value?x.value[w.value]:null,o=V(e(x.value.slice()),(e=>m(e.dataRef.domRef))),n=t?o.indexOf(t):null;return-1===n&&(n=null),{options:o,activeOptionIndex:n}}let C=(0,e.computed)((()=>o.multiple?1:0)),R=(0,e.computed)((()=>o.nullable)),[k,E]=function(t,o,n){let a=(0,e.ref)(null==n?void 0:n.value),r=(0,e.computed)((()=>void 0!==t.value));return[(0,e.computed)((()=>r.value?t.value:a.value)),function(e){return r.value||(a.value=e),null==o?void 0:o(e)}]}((0,e.computed)((()=>void 0===o.modelValue?t(C.value,{1:[],0:void 0}):o.modelValue)),(e=>r("update:modelValue",e)),(0,e.computed)((()=>o.defaultValue))),N={comboboxState:i,value:k,mode:C,compare(e,t){if("string"==typeof o.by){let n=o.by;return(null==e?void 0:e[n])===(null==t?void 0:t[n])}return o.by(e,t)},defaultValue:(0,e.computed)((()=>o.defaultValue)),nullable:R,inputRef:s,labelRef:u,buttonRef:d,optionsRef:p,disabled:(0,e.computed)((()=>o.disabled)),options:x,change(e){E(e)},activeOptionIndex:(0,e.computed)((()=>{if(O.value&&null===w.value&&x.value.length>0){let e=x.value.findIndex((e=>!e.dataRef.disabled));if(-1!==e)return e}return w.value})),activationTrigger:y,optionsPropsRef:v,closeCombobox(){O.value=!1,!o.disabled&&1!==i.value&&(i.value=1,w.value=null)},openCombobox(){if(O.value=!0,o.disabled||0===i.value)return;let n=x.value.findIndex((o=>{let n=(0,e.toRaw)(o.dataRef.value);return t(C.value,{0:()=>N.compare((0,e.toRaw)(N.value.value),(0,e.toRaw)(n)),1:()=>(0,e.toRaw)(N.value.value).some((t=>N.compare((0,e.toRaw)(t),(0,e.toRaw)(n))))})}));-1!==n&&(w.value=n),i.value=0},goToOption(e,t,n){if(O.value=!1,o.disabled||p.value&&!v.value.static&&1===i.value)return;let a=S();if(null===a.activeOptionIndex){let e=a.options.findIndex((e=>!e.dataRef.disabled));-1!==e&&(a.activeOptionIndex=e)}let r=b(e===f.Specific?{focus:f.Specific,id:t}:{focus:e},{resolveItems:()=>a.options,resolveActiveIndex:()=>a.activeOptionIndex,resolveId:e=>e.id,resolveDisabled:e=>e.dataRef.disabled});w.value=r,y.value=null!=n?n:1,x.value=a.options},selectOption(o){let n=x.value.find((e=>e.id===o));if(!n)return;let{dataRef:a}=n;E(t(C.value,{0:()=>a.value,1:()=>{let t=(0,e.toRaw)(N.value.value).slice(),o=(0,e.toRaw)(a.value),n=t.findIndex((t=>N.compare(o,(0,e.toRaw)(t))));return-1===n?t.push(o):t.splice(n,1),t}}))},selectActiveOption(){if(null===N.activeOptionIndex.value)return;let{dataRef:o,id:n}=x.value[N.activeOptionIndex.value];E(t(C.value,{0:()=>o.value,1:()=>{let t=(0,e.toRaw)(N.value.value).slice(),n=(0,e.toRaw)(o.value),a=t.findIndex((t=>N.compare(n,(0,e.toRaw)(t))));return-1===a?t.push(n):t.splice(a,1),t}})),N.goToOption(f.Specific,n)},registerOption(o,n){let a={id:o,dataRef:n},r=S((e=>[...e,a]));if(null===w.value){let o=n.value.value;t(C.value,{0:()=>N.compare((0,e.toRaw)(N.value.value),(0,e.toRaw)(o)),1:()=>(0,e.toRaw)(N.value.value).some((t=>N.compare((0,e.toRaw)(t),(0,e.toRaw)(o))))})&&(r.activeOptionIndex=r.options.indexOf(a))}x.value=r.options,w.value=r.activeOptionIndex,y.value=1},unregisterOption(e){var t;null!==N.activeOptionIndex.value&&(null==(t=N.options.value[N.activeOptionIndex.value])?void 0:t.id)===e&&(O.value=!0);let o=S((t=>{let o=t.findIndex((t=>t.id===e));return-1!==o&&t.splice(o,1),t}));x.value=o.options,w.value=o.activeOptionIndex,y.value=1}};j([s,d,p],(()=>N.closeCombobox()),(0,e.computed)((()=>0===i.value))),(0,e.provide)(W,N),function(t){(0,e.provide)(g,t)}((0,e.computed)((()=>t(i.value,{0:h.Open,1:h.Closed}))));let I=(0,e.computed)((()=>null===N.activeOptionIndex.value?null:x.value[N.activeOptionIndex.value].dataRef.value)),P=(0,e.computed)((()=>{var e;return null==(e=m(s))?void 0:e.closest("form")}));return(0,e.onMounted)((()=>{(0,e.watch)([P],(()=>{if(P.value&&void 0!==o.defaultValue)return P.value.addEventListener("reset",e),()=>{var t;null==(t=P.value)||t.removeEventListener("reset",e)};function e(){N.change(o.defaultValue)}}),{immediate:!0})})),()=>{let{name:t,disabled:r,...u}=o,s={open:0===i.value,disabled:r,activeIndex:N.activeOptionIndex.value,activeOption:I.value,value:k.value};return(0,e.h)(e.Fragment,[...null!=t&&null!=k.value?_({[t]:k.value}).map((([t,o])=>(0,e.h)(M,function(e){let t=Object.assign({},e);for(let e in t)void 0===t[e]&&delete t[e];return t}({features:L.Hidden,key:t,as:"input",type:"hidden",hidden:!0,readOnly:!0,name:t,value:o})))):[],l({theirProps:{...a,...c(u,["modelValue","defaultValue","nullable","multiple","onUpdate:modelValue","by"])},ourProps:{},slot:s,slots:n,attrs:a,name:"Combobox"})])}}}),Z=((0,e.defineComponent)({name:"ComboboxLabel",props:{as:{type:[Object,String],default:"label"},id:{type:String,default:()=>`headlessui-combobox-label-${p()}`}},setup(e,{attrs:t,slots:o}){let n=K("ComboboxLabel");function a(){var e;null==(e=m(n.inputRef))||e.focus({preventScroll:!0})}return()=>{let r={open:0===n.comboboxState.value,disabled:n.disabled.value},{id:i,...u}=e;return l({ourProps:{id:i,ref:n.labelRef,onClick:a},theirProps:u,slot:r,attrs:t,slots:o,name:"ComboboxLabel"})}}}),(0,e.defineComponent)({name:"ComboboxButton",props:{as:{type:[Object,String],default:"button"},id:{type:String,default:()=>`headlessui-combobox-button-${p()}`}},setup(t,{attrs:o,slots:n,expose:a}){let r=K("ComboboxButton");function i(t){r.disabled.value||(0===r.comboboxState.value?r.closeCombobox():(t.preventDefault(),r.openCombobox()),(0,e.nextTick)((()=>{var e;return null==(e=m(r.inputRef))?void 0:e.focus({preventScroll:!0})})))}function u(t){switch(t.key){case v.ArrowDown:return t.preventDefault(),t.stopPropagation(),1===r.comboboxState.value&&r.openCombobox(),void(0,e.nextTick)((()=>{var e;return null==(e=r.inputRef.value)?void 0:e.focus({preventScroll:!0})}));case v.ArrowUp:return t.preventDefault(),t.stopPropagation(),1===r.comboboxState.value&&(r.openCombobox(),(0,e.nextTick)((()=>{r.value.value||r.goToOption(f.Last)}))),void(0,e.nextTick)((()=>{var e;return null==(e=r.inputRef.value)?void 0:e.focus({preventScroll:!0})}));case v.Escape:if(0!==r.comboboxState.value)return;return t.preventDefault(),r.optionsRef.value&&!r.optionsPropsRef.value.static&&t.stopPropagation(),r.closeCombobox(),void(0,e.nextTick)((()=>{var e;return null==(e=r.inputRef.value)?void 0:e.focus({preventScroll:!0})}))}}a({el:r.buttonRef,$el:r.buttonRef});let s=function(t,o){let n=(0,e.ref)(w(t.value.type,t.value.as));return(0,e.onMounted)((()=>{n.value=w(t.value.type,t.value.as)})),(0,e.watchEffect)((()=>{var e;n.value||m(o)&&m(o)instanceof HTMLButtonElement&&(null==(e=m(o))||!e.hasAttribute("type"))&&(n.value="button")})),n}((0,e.computed)((()=>({as:t.as,type:o.type}))),r.buttonRef);return()=>{var e,a;let c={open:0===r.comboboxState.value,disabled:r.disabled.value,value:r.value.value},{id:d,...p}=t;return l({ourProps:{ref:r.buttonRef,id:d,type:s.value,tabindex:"-1","aria-haspopup":"listbox","aria-controls":null==(e=m(r.optionsRef))?void 0:e.id,"aria-expanded":r.disabled.value?void 0:0===r.comboboxState.value,"aria-labelledby":r.labelRef.value?[null==(a=m(r.labelRef))?void 0:a.id,d].join(" "):void 0,disabled:!0===r.disabled.value||void 0,onKeydown:u,onClick:i},theirProps:p,slot:c,attrs:o,slots:n,name:"ComboboxButton"})}}})),G=(0,e.defineComponent)({name:"ComboboxInput",props:{as:{type:[Object,String],default:"input"},static:{type:Boolean,default:!1},unmount:{type:Boolean,default:!0},displayValue:{type:Function},defaultValue:{type:String,default:void 0},id:{type:String,default:()=>`headlessui-combobox-input-${p()}`}},emits:{change:e=>!0},setup(o,{emit:n,attrs:r,slots:i,expose:u}){let s=K("ComboboxInput"),c={value:!1};u({el:s.inputRef,$el:s.inputRef});let d=(0,e.computed)((()=>{var e;let t=s.value.value;return m(s.inputRef)?void 0!==o.displayValue&&void 0!==t?null!=(e=o.displayValue(t))?e:"":"string"==typeof t?t:"":""}));(0,e.onMounted)((()=>{(0,e.watch)([d,s.comboboxState],(([e,t],[o,n])=>{if(c.value)return;let a=m(s.inputRef);a&&(0===n&&1===t||e!==o)&&(a.value=e)}),{immediate:!0}),(0,e.watch)([s.comboboxState],(([e],[t])=>{if(0===e&&1===t){let e=m(s.inputRef);if(!e)return;let t=e.value,{selectionStart:o,selectionEnd:n,selectionDirection:a}=e;e.value="",e.value=t,null!==a?e.setSelectionRange(o,n,a):e.setSelectionRange(o,n)}}))}));let p=(0,e.ref)(!1);function b(){p.value=!0}function g(){setTimeout((()=>{p.value=!1}))}function h(o){switch(c.value=!0,o.key){case v.Backspace:case v.Delete:if(0!==s.mode.value||!s.nullable.value)return;let n=o.currentTarget;requestAnimationFrame((()=>{if(""===n.value){s.change(null);let e=m(s.optionsRef);e&&(e.scrollTop=0),s.goToOption(f.Nothing)}}));break;case v.Enter:if(c.value=!1,0!==s.comboboxState.value||p.value)return;if(o.preventDefault(),o.stopPropagation(),null===s.activeOptionIndex.value)return void s.closeCombobox();s.selectActiveOption(),0===s.mode.value&&s.closeCombobox();break;case v.ArrowDown:return c.value=!1,o.preventDefault(),o.stopPropagation(),t(s.comboboxState.value,{0:()=>s.goToOption(f.Next),1:()=>s.openCombobox()});case v.ArrowUp:return c.value=!1,o.preventDefault(),o.stopPropagation(),t(s.comboboxState.value,{0:()=>s.goToOption(f.Previous),1:()=>{s.openCombobox(),(0,e.nextTick)((()=>{s.value.value||s.goToOption(f.Last)}))}});case v.Home:if(o.shiftKey)break;return c.value=!1,o.preventDefault(),o.stopPropagation(),s.goToOption(f.First);case v.PageUp:return c.value=!1,o.preventDefault(),o.stopPropagation(),s.goToOption(f.First);case v.End:if(o.shiftKey)break;return c.value=!1,o.preventDefault(),o.stopPropagation(),s.goToOption(f.Last);case v.PageDown:return c.value=!1,o.preventDefault(),o.stopPropagation(),s.goToOption(f.Last);case v.Escape:if(c.value=!1,0!==s.comboboxState.value)return;o.preventDefault(),s.optionsRef.value&&!s.optionsPropsRef.value.static&&o.stopPropagation(),s.closeCombobox();break;case v.Tab:if(c.value=!1,0!==s.comboboxState.value)return;0===s.mode.value&&s.selectActiveOption(),s.closeCombobox()}}function x(e){s.openCombobox(),n("change",e)}function w(){c.value=!1}let y=(0,e.computed)((()=>{var e,t,n,a;return null!=(a=null!=(n=null!=(t=o.defaultValue)?t:void 0!==s.defaultValue.value?null==(e=o.displayValue)?void 0:e.call(o,s.defaultValue.value):null)?n:s.defaultValue.value)?a:""}));return()=>{var e,t,n,u,c,d;let p={open:0===s.comboboxState.value},{id:v,displayValue:f,onChange:O,...S}=o;return l({ourProps:{"aria-controls":null==(e=s.optionsRef.value)?void 0:e.id,"aria-expanded":s.disabled.value?void 0:0===s.comboboxState.value,"aria-activedescendant":null===s.activeOptionIndex.value||null==(t=s.options.value[s.activeOptionIndex.value])?void 0:t.id,"aria-labelledby":null!=(c=null==(n=m(s.labelRef))?void 0:n.id)?c:null==(u=m(s.buttonRef))?void 0:u.id,"aria-autocomplete":"list",id:v,onCompositionstart:b,onCompositionend:g,onKeydown:h,onInput:x,onBlur:w,role:"combobox",type:null!=(d=r.type)?d:"text",tabIndex:0,ref:s.inputRef,defaultValue:y.value},theirProps:S,slot:p,attrs:r,slots:i,features:a.RenderStrategy|a.Static,name:"ComboboxInput"})}}}),J=(0,e.defineComponent)({name:"ComboboxOptions",props:{as:{type:[Object,String],default:"ul"},static:{type:Boolean,default:!1},unmount:{type:Boolean,default:!0},hold:{type:[Boolean],default:!1}},setup(t,{attrs:o,slots:n,expose:r}){let i=K("ComboboxOptions"),u=`headlessui-combobox-options-${p()}`;r({el:i.optionsRef,$el:i.optionsRef}),(0,e.watchEffect)((()=>{i.optionsPropsRef.value.static=t.static})),(0,e.watchEffect)((()=>{i.optionsPropsRef.value.hold=t.hold}));let s=x(),d=(0,e.computed)((()=>null!==s?(s.value&h.Open)===h.Open:0===i.comboboxState.value));return function({container:t,accept:o,walk:n,enabled:a}){(0,e.watchEffect)((()=>{let e=t.value;if(!e||void 0!==a&&!a.value)return;let r=C(t);if(!r)return;let l=Object.assign((e=>o(e)),{acceptNode:o}),i=r.createTreeWalker(e,NodeFilter.SHOW_ELEMENT,l,!1);for(;i.nextNode();)n(i.currentNode)}))}({container:(0,e.computed)((()=>m(i.optionsRef))),enabled:(0,e.computed)((()=>0===i.comboboxState.value)),accept:e=>"option"===e.getAttribute("role")?NodeFilter.FILTER_REJECT:e.hasAttribute("role")?NodeFilter.FILTER_SKIP:NodeFilter.FILTER_ACCEPT,walk(e){e.setAttribute("role","none")}}),()=>{var e,r,s;let p={open:0===i.comboboxState.value};return l({ourProps:{"aria-labelledby":null!=(s=null==(e=m(i.labelRef))?void 0:e.id)?s:null==(r=m(i.buttonRef))?void 0:r.id,id:u,ref:i.optionsRef,role:"listbox","aria-multiselectable":1===i.mode.value||void 0},theirProps:c(t,["hold"]),slot:p,attrs:o,slots:n,features:a.RenderStrategy|a.Static,visible:d.value,name:"ComboboxOptions"})}}}),Y=(0,e.defineComponent)({name:"ComboboxOption",props:{as:{type:[Object,String],default:"li"},value:{type:[Object,String,Number,Boolean]},disabled:{type:Boolean,default:!1}},setup(o,{slots:n,attrs:a,expose:r}){let i=K("ComboboxOption"),u=`headlessui-combobox-option-${p()}`,s=(0,e.ref)(null);r({el:s,$el:s});let c=(0,e.computed)((()=>null!==i.activeOptionIndex.value&&i.options.value[i.activeOptionIndex.value].id===u)),d=(0,e.computed)((()=>t(i.mode.value,{0:()=>i.compare((0,e.toRaw)(i.value.value),(0,e.toRaw)(o.value)),1:()=>(0,e.toRaw)(i.value.value).some((t=>i.compare((0,e.toRaw)(t),(0,e.toRaw)(o.value))))}))),v=(0,e.computed)((()=>({disabled:o.disabled,value:o.value,domRef:s})));function b(e){if(o.disabled)return e.preventDefault();i.selectOption(u),0===i.mode.value&&i.closeCombobox(),F()||requestAnimationFrame((()=>{var e;return null==(e=m(i.inputRef))?void 0:e.focus()}))}function g(){if(o.disabled)return i.goToOption(f.Nothing);i.goToOption(f.Specific,u)}(0,e.onMounted)((()=>i.registerOption(u,v))),(0,e.onUnmounted)((()=>i.unregisterOption(u))),(0,e.watchEffect)((()=>{0===i.comboboxState.value&&c.value&&0!==i.activationTrigger.value&&(0,e.nextTick)((()=>{var e,t;return null==(t=null==(e=m(s))?void 0:e.scrollIntoView)?void 0:t.call(e,{block:"nearest"})}))}));let h=function(){let t=(0,e.ref)([-1,-1]);return{wasMoved(e){let o=D(e);return(t.value[0]!==o[0]||t.value[1]!==o[1])&&(t.value=o,!0)},update(e){t.value=D(e)}}}();function x(e){h.update(e)}function w(e){h.wasMoved(e)&&(o.disabled||c.value||i.goToOption(f.Specific,u,0))}function y(e){h.wasMoved(e)&&(o.disabled||c.value&&(i.optionsPropsRef.value.hold||i.goToOption(f.Nothing)))}return()=>{let{disabled:e}=o,t={active:c.value,selected:d.value,disabled:e};return l({ourProps:{id:u,ref:s,role:"option",tabIndex:!0===e?void 0:-1,"aria-disabled":!0===e||void 0,"aria-selected":d.value,disabled:void 0,onClick:b,onFocus:g,onPointerenter:x,onMouseenter:x,onPointermove:w,onMousemove:w,onPointerleave:y,onMouseleave:y},theirProps:o,slot:t,attrs:a,slots:n,name:"ComboboxOption"})}}});var X=function(t){return(0,e.pushScopeId)("data-v-4ef21301"),t=t(),(0,e.popScopeId)(),t},Q=[X((function(){return(0,e.createElementVNode)("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24","stroke-width":"1.5",stroke:"currentColor",width:"12",height:"12"},[(0,e.createElementVNode)("path",{"stroke-linecap":"round","stroke-linejoin":"round",d:"M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9m5.25 11.25h-4.5m4.5 0v-4.5m0 4.5L15 15"})],-1)}))],ee=[X((function(){return(0,e.createElementVNode)("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24","stroke-width":"1.5",stroke:"currentColor",width:"12",height:"12"},[(0,e.createElementVNode)("path",{"stroke-linecap":"round","stroke-linejoin":"round",d:"M9 9V4.5M9 9H4.5M9 9 3.75 3.75M9 15v4.5M9 15H4.5M9 15l-5.25 5.25M15 9h4.5M15 9V4.5M15 9l5.25-5.25M15 15h4.5M15 15v4.5m0-4.5 5.25 5.25"})],-1)}))],te={class:"relative"},oe=["aria-label","onClick"],ne=X((function(){return(0,e.createElementVNode)("span",null,[(0,e.createElementVNode)("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 20 20",fill:"currentColor"},[(0,e.createElementVNode)("path",{"fill-rule":"evenodd",d:"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z","clip-rule":"evenodd"})])],-1)})),ae=X((function(){return(0,e.createElementVNode)("svg",{class:"flex-shrink-0 pointer-events-none form-select-arrow",xmlns:"http://www.w3.org/2000/svg",width:"10",height:"6",viewBox:"0 0 10 6"},[(0,e.createElementVNode)("path",{class:"fill-current",d:"M8.292893.292893c.390525-.390524 1.023689-.390524 1.414214 0 .390524.390525.390524 1.023689 0 1.414214l-4 4c-.390525.390524-1.023689.390524-1.414214 0l-4-4c-.390524-.390525-.390524-1.023689 0-1.414214.390525-.390524 1.023689-.390524 1.414214 0L5 3.585786 8.292893.292893z"})],-1)})),re=[X((function(){return(0,e.createElementVNode)("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 20 20",fill:"currentColor","aria-hidden":"true",class:"w-5 h-5"},[(0,e.createElementVNode)("path",{"fill-rule":"evenodd",d:"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z","clip-rule":"evenodd"})],-1)}))];const le={__name:"ComboboxFilter",props:{resourceName:{type:String,required:!0},filterKey:{type:String,required:!0},lens:String},emits:["change"],setup:function(t,o){var n=o.emit,a=t,r=(0,e.inject)("store"),l=(0,e.toRef)(a,"resourceName"),i=(0,e.toRef)(a,"filterKey"),u=((0,e.toRef)(a,"lens"),(0,e.ref)(!1)),s=(0,e.ref)(""),c=(0,e.ref)([]),d=(0,e.ref)(null),p=(0,e.computed)((function(){return r.getters["".concat(l.value,"/getFilter")](i.value)})),v=(0,e.computed)((function(){return p.value.options})),f=(0,e.computed)((function(){return v.value.filter((function(e){return c.value.includes(e.value)}))})),b=(0,e.computed)((function(){return""===s.value?v.value:v.value.filter((function(e){return""===s.value||e.label.toLowerCase().includes(s.value.toLowerCase())}))}));function m(){u.value=!u.value,Nova.$emit("nova-combobox-filter-options-wide",u.value)}function g(e){u.value=e}function h(){c.value=Array.from(p.value.currentValue)}function x(){r.commit("".concat(l.value,"/updateFilterState"),{filterClass:i.value,value:Array.isArray(c.value)&&0===c.value.length?"":c.value}),n("change")}function w(e){e.target.matches("button")||d.value.nextElementSibling.focus()}return(0,e.watch)(c,(function(){x(),s.value=""})),h(),(0,e.onMounted)((function(){Nova.$on("filter-reset",h),Nova.$on("nova-combobox-filter-options-wide",g)})),(0,e.onBeforeUnmount)((function(){Nova.$off("filter-reset",h),Nova.$off("nova-combobox-filter-options-wide",g)})),function(t,o){var n=(0,e.resolveComponent)("FilterContainer");return(0,e.openBlock)(),(0,e.createBlock)(n,null,{filter:(0,e.withCtx)((function(){return[(0,e.createElementVNode)("div",te,[(0,e.createVNode)((0,e.unref)(q),{modelValue:(0,e.unref)(c),"onUpdate:modelValue":o[1]||(o[1]=function(t){return(0,e.isRef)(c)?c.value=t:c=t}),multiple:""},{default:(0,e.withCtx)((function(){return[(0,e.createElementVNode)("div",{class:"combobox-input-container",onClick:w},[(0,e.createElementVNode)("div",{class:"selected-option-button-list",ref_key:"selectedOptionListEl",ref:d},[((0,e.openBlock)(!0),(0,e.createElementBlock)(e.Fragment,null,(0,e.renderList)((0,e.unref)(f),(function(t){return(0,e.openBlock)(),(0,e.createElementBlock)("button",{"aria-label":"remove "+t.label,class:"selected-option-button",onClick:function(e){return o=t.value,n=c.value.indexOf(o),c.value.splice(n,1),void x();var o,n}},[(0,e.createElementVNode)("span",null,(0,e.toDisplayString)(t.label),1),ne],8,oe)})),256))],512),(0,e.createVNode)((0,e.unref)(G),{onInput:o[0]||(o[0]=function(t){return(0,e.isRef)(s)?s.value=t.target.value:s=t.target.value}),class:(0,e.normalizeClass)(["combobox-input",{"combobox-input--has-selected-options":(0,e.unref)(f).length>0}])},null,8,["class"]),(0,e.createVNode)((0,e.unref)(Z),{class:"combobox-button"},{default:(0,e.withCtx)((function(){return[ae]})),_:1})]),(0,e.createVNode)((0,e.unref)(J),{class:"combobox-options"},{default:(0,e.withCtx)((function(){return[((0,e.openBlock)(!0),(0,e.createElementBlock)(e.Fragment,null,(0,e.renderList)((0,e.unref)(b),(function(t){return(0,e.openBlock)(),(0,e.createBlock)((0,e.unref)(Y),{key:t.value,value:t.value,as:"template"},{default:(0,e.withCtx)((function(o){var n=o.active,a=o.selected;return[(0,e.createElementVNode)("li",{class:(0,e.normalizeClass)(["combobox-option",{"combobox-option--is-active":n,"combobox-option--is-selected":a}])},[(0,e.createElementVNode)("span",{class:(0,e.normalizeClass)(["combobox-option__label",{"combobox-option__label--wide":(0,e.unref)(u)}])},(0,e.toDisplayString)(t.label),3),(0,e.createElementVNode)("span",{class:(0,e.normalizeClass)(["combobox-option__icon",{"combobox-option__icon--is-selected":a}])},re,2)],2)]})),_:2},1032,["value"])})),128))]})),_:1})]})),_:1},8,["modelValue"])])]})),default:(0,e.withCtx)((function(){return[(0,e.createElementVNode)("span",null,[(0,e.createTextVNode)((0,e.toDisplayString)((0,e.unref)(p).name)+" ",1),(0,e.unref)(u)?(0,e.createCommentVNode)("",!0):((0,e.openBlock)(),(0,e.createElementBlock)("button",{key:0,class:"wide-narrow-btn",onClick:m,"aria-label":"wide"},Q)),(0,e.unref)(u)?((0,e.openBlock)(),(0,e.createElementBlock)("button",{key:1,class:"wide-narrow-btn",onClick:m,"aria-label":"narrow"},ee)):(0,e.createCommentVNode)("",!0)])]})),_:1})}}};var ie=o(379),ue=o.n(ie),se=o(739),ce={insert:"head",singleton:!1};ue()(se.Z,ce);se.Z.locals;const de=(0,o(744).Z)(le,[["__scopeId","data-v-4ef21301"]]);Nova.booting((function(e,t){e.component("nova-combobox-filter",de)}))})()})(); -------------------------------------------------------------------------------- /dist/mix-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "/js/filter.js": "/js/filter.js" 3 | } 4 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Harrald Torenvlied 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /mix.js: -------------------------------------------------------------------------------- 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()) 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "npm run development", 5 | "development": "mix", 6 | "watch": "mix watch", 7 | "watch-poll": "mix watch -- --watch-options-poll=1000", 8 | "hot": "mix watch --hot", 9 | "prod": "npm run production", 10 | "production": "mix --production" 11 | }, 12 | "devDependencies": { 13 | "@vue/compiler-sfc": "^3.2.22", 14 | "laravel-mix": "^6.0.41", 15 | "postcss": "^8.3.11", 16 | "sass-loader": "^12.1.0", 17 | "vue-loader": "^16.8.3" 18 | }, 19 | "dependencies": { 20 | "@headlessui/vue": "^1.6.0", 21 | "sass": "^1.43.4" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Nova Combobox Filter 2 | 3 | 4 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/harrald/nova-combobox-filter.svg?style=flat-square)](https://packagist.org/packages/harrald/nova-combobox-filter) 5 | [![Total Downloads](https://img.shields.io/packagist/php-v/harrald/nova-combobox-filter.svg?style=flat-square)](https://packagist.org/packages/harrald/nova-combobox-filter) 6 | [![Total Downloads](https://img.shields.io/packagist/dt/harrald/nova-combobox-filter.svg?style=flat-square)](https://packagist.org/packages/harrald/nova-combobox-filter) 7 | [![Total Downloads](https://img.shields.io/packagist/l/harrald/nova-combobox-filter.svg?style=flat-square)](https://packagist.org/packages/harrald/nova-combobox-filter) 8 | 9 | 10 | This component is based on the [Headless UI Combobox component](https://headlessui.dev/vue/combobox) and adds a multiselect filter to Laravel Nova. 11 | 12 | ## Requirements 13 | - `php: >=7.3` 14 | - `laravel/nova: ^4.0` 15 | 16 | ## Installation 17 | 18 | Install via Composer: 19 | 20 | ```bash 21 | composer require harrald/nova-combobox-filter 22 | ``` 23 | 24 | ## Usage 25 | 26 | The `nova-combobox-filter` extends the [Nova select filter](https://nova.laravel.com/docs/4.0/filters/defining-filters.html#select-filters). And thus follows the same logic as to how to implement it. 27 | 28 | ### Filter 29 | Make a new Filter class in `App/Nova/Filters` and let it extend the `\Harrald\NovaComboboxFilter\NovaComboboxFilter` class. 30 | 31 | Use the following format: 32 | ```php 33 | 'open', 65 | 'Paid' => 'paid', 66 | ]); 67 | } 68 | } 69 | ``` 70 | 71 | - The property `$name` is the displayable name of the filter. 72 | - The method `columnName` must return the name of the column to filter on 73 | - The `options` method should return a `Illuminate\Support\Collection` with key/value pairs. 74 | 75 | 76 | With Dynamic options 77 | ```php 78 | public function options(Request $request): Collection 79 | { 80 | return User::all()->pluck('id', 'name'); 81 | } 82 | ``` 83 | 84 | ### Resource 85 | Use the new filter in you Resource. Follows the same logic as a any other [Nova filter](https://nova.laravel.com/docs/4.0/customization/filters.html#registering-filters) 86 | ```php 87 | /** 88 | * Get the filters available for the resource. 89 | * 90 | * @param NovaRequest $request 91 | * @return array 92 | */ 93 | public function filters(NovaRequest $request): array 94 | { 95 | return [ 96 | OpenPaidStateFilter::make(), 97 | ]; 98 | } 99 | ``` 100 | 101 | 102 | 103 | ## Screenshots 104 | multi options selected 105 | 106 | filter options by typing 107 | 108 | 109 | ## Credits 110 | 111 | This package was inspired by [optimistdigital/nova-multiselect-filter](https://github.com/optimistdigital/nova-multiselect-filter) 112 | 113 | ## License 114 | 115 | This project is open-sourced software licensed under the MIT license. 116 | -------------------------------------------------------------------------------- /readme/img/multiselect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Harrald/nova-combobox-filter/2fac8af7c79ef231ce81e853676a9527364c2490/readme/img/multiselect.png -------------------------------------------------------------------------------- /readme/img/typing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Harrald/nova-combobox-filter/2fac8af7c79ef231ce81e853676a9527364c2490/readme/img/typing.png -------------------------------------------------------------------------------- /resources/js/components/ComboboxFilter.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | 143 | 144 | 337 | -------------------------------------------------------------------------------- /resources/js/filter.js: -------------------------------------------------------------------------------- 1 | import ComboboxFilter from './components/ComboboxFilter' 2 | 3 | Nova.booting((app, store) => { 4 | app.component('nova-combobox-filter', ComboboxFilter) 5 | }) 6 | -------------------------------------------------------------------------------- /src/FilterServiceProvider.php: -------------------------------------------------------------------------------- 1 | whereIn($this->columnName(), $value); 30 | } 31 | 32 | /** 33 | * Get the filter's available options. 34 | * 35 | * @param NovaRequest $request 36 | * @return array|Collection 37 | */ 38 | public function options(NovaRequest $request) 39 | { 40 | return [ 41 | 'Foo' => 'foo', 42 | 'Bar' => 'bar', 43 | ]; 44 | } 45 | 46 | abstract protected function columnName(): string; 47 | } 48 | -------------------------------------------------------------------------------- /webpack.mix.js: -------------------------------------------------------------------------------- 1 | let mix = require('laravel-mix') 2 | 3 | require('./mix') 4 | 5 | mix 6 | .setPublicPath('dist') 7 | .js('resources/js/filter.js', 'js') 8 | .vue({ version: 3 }) 9 | .nova('all4running/nova-multi-select-filter') 10 | --------------------------------------------------------------------------------