├── .gitignore ├── README.md ├── composer.json ├── dist └── js │ └── custom-filters.js ├── mix-manifest.json ├── package.json ├── resources ├── airbnb-modified.css └── js │ ├── components │ ├── DatePicker.vue │ ├── FilterSelector.vue │ └── SelectFilter.vue │ └── index.js ├── src ├── CustomFilter.php ├── DateFilter.php └── FieldServiceProvider.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 | # DEPRECATED 2 | 3 | Since Nova 1.1.8 this feature has been adopted to the core. You should upgrade Nova and use that filter instead. 4 | 5 | ## Date Filter for Laravel Nova 6 | 7 | Nova filter that displays a Date Picker instead of a select. 8 | 9 | ### Demo 10 | 11 | ![Demo](http://g.recordit.co/7sG50yEf7O.gif) 12 | 13 | ### Install 14 | 15 | Run this command in your nova project: 16 | `composer require 64robots/nova-date-filter` 17 | 18 | ### How to use 19 | 20 | Just use DateFilter class instead of Filter 21 | 22 | ```php 23 | use R64\Filters\DateFilter; 24 | 25 | class DateFrom extends DateFilter 26 | { 27 | // 28 | } 29 | ``` 30 | 31 | ### Customization 32 | 33 | As Date Filter is not a select anymore we can use options method to pass the date picker config 34 | 35 | ```php 36 | use R64\Filters\DateFilter; 37 | 38 | class DateFrom extends DateFilter 39 | { 40 | //... 41 | 42 | public function options(Request $request) 43 | { 44 | return [ 45 | 'dateFormat' => 'Y-m-d', // default Y-m-d H:i:S 46 | 'placeholder' => 'My placeholder', // default __('Pick a date') 47 | 'disabled' => true, // default false 48 | 'twelveHourTime' => true, // default false 49 | 'enableTime' => true, // default false 50 | 'enableSeconds' => true, // default false 51 | ]; 52 | } 53 | } 54 | ``` 55 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "64robots/nova-date-filter", 3 | "description": "A Laravel Nova date filter.", 4 | "keywords": [ 5 | "laravel", 6 | "nova", 7 | "date", 8 | "filter" 9 | ], 10 | "license": "MIT", 11 | "require": { 12 | "php": ">=7.1.0" 13 | }, 14 | "autoload": { 15 | "psr-4": { 16 | "R64\\Filters\\": "src/" 17 | } 18 | }, 19 | "extra": { 20 | "laravel": { 21 | "providers": [ 22 | "R64\\Filters\\FieldServiceProvider" 23 | ] 24 | } 25 | }, 26 | "config": { 27 | "sort-packages": true 28 | }, 29 | "minimum-stability": "dev", 30 | "prefer-stable": true 31 | } 32 | -------------------------------------------------------------------------------- /dist/js/custom-filters.js: -------------------------------------------------------------------------------- 1 | /******/ (function(modules) { // webpackBootstrap 2 | /******/ // The module cache 3 | /******/ var installedModules = {}; 4 | /******/ 5 | /******/ // The require function 6 | /******/ function __webpack_require__(moduleId) { 7 | /******/ 8 | /******/ // Check if module is in cache 9 | /******/ if(installedModules[moduleId]) { 10 | /******/ return installedModules[moduleId].exports; 11 | /******/ } 12 | /******/ // Create a new module (and put it into the cache) 13 | /******/ var module = installedModules[moduleId] = { 14 | /******/ i: moduleId, 15 | /******/ l: false, 16 | /******/ exports: {} 17 | /******/ }; 18 | /******/ 19 | /******/ // Execute the module function 20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 21 | /******/ 22 | /******/ // Flag the module as loaded 23 | /******/ module.l = true; 24 | /******/ 25 | /******/ // Return the exports of the module 26 | /******/ return module.exports; 27 | /******/ } 28 | /******/ 29 | /******/ 30 | /******/ // expose the modules object (__webpack_modules__) 31 | /******/ __webpack_require__.m = modules; 32 | /******/ 33 | /******/ // expose the module cache 34 | /******/ __webpack_require__.c = installedModules; 35 | /******/ 36 | /******/ // define getter function for harmony exports 37 | /******/ __webpack_require__.d = function(exports, name, getter) { 38 | /******/ if(!__webpack_require__.o(exports, name)) { 39 | /******/ Object.defineProperty(exports, name, { 40 | /******/ configurable: false, 41 | /******/ enumerable: true, 42 | /******/ get: getter 43 | /******/ }); 44 | /******/ } 45 | /******/ }; 46 | /******/ 47 | /******/ // getDefaultExport function for compatibility with non-harmony modules 48 | /******/ __webpack_require__.n = function(module) { 49 | /******/ var getter = module && module.__esModule ? 50 | /******/ function getDefault() { return module['default']; } : 51 | /******/ function getModuleExports() { return module; }; 52 | /******/ __webpack_require__.d(getter, 'a', getter); 53 | /******/ return getter; 54 | /******/ }; 55 | /******/ 56 | /******/ // Object.prototype.hasOwnProperty.call 57 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 58 | /******/ 59 | /******/ // __webpack_public_path__ 60 | /******/ __webpack_require__.p = ""; 61 | /******/ 62 | /******/ // Load entry module and return exports 63 | /******/ return __webpack_require__(__webpack_require__.s = 2); 64 | /******/ }) 65 | /************************************************************************/ 66 | /******/ ([ 67 | /* 0 */ 68 | /***/ (function(module, exports) { 69 | 70 | /* globals __VUE_SSR_CONTEXT__ */ 71 | 72 | // IMPORTANT: Do NOT use ES2015 features in this file. 73 | // This module is a runtime utility for cleaner component module output and will 74 | // be included in the final webpack user bundle. 75 | 76 | module.exports = function normalizeComponent ( 77 | rawScriptExports, 78 | compiledTemplate, 79 | functionalTemplate, 80 | injectStyles, 81 | scopeId, 82 | moduleIdentifier /* server only */ 83 | ) { 84 | var esModule 85 | var scriptExports = rawScriptExports = rawScriptExports || {} 86 | 87 | // ES6 modules interop 88 | var type = typeof rawScriptExports.default 89 | if (type === 'object' || type === 'function') { 90 | esModule = rawScriptExports 91 | scriptExports = rawScriptExports.default 92 | } 93 | 94 | // Vue.extend constructor export interop 95 | var options = typeof scriptExports === 'function' 96 | ? scriptExports.options 97 | : scriptExports 98 | 99 | // render functions 100 | if (compiledTemplate) { 101 | options.render = compiledTemplate.render 102 | options.staticRenderFns = compiledTemplate.staticRenderFns 103 | options._compiled = true 104 | } 105 | 106 | // functional template 107 | if (functionalTemplate) { 108 | options.functional = true 109 | } 110 | 111 | // scopedId 112 | if (scopeId) { 113 | options._scopeId = scopeId 114 | } 115 | 116 | var hook 117 | if (moduleIdentifier) { // server build 118 | hook = function (context) { 119 | // 2.3 injection 120 | context = 121 | context || // cached call 122 | (this.$vnode && this.$vnode.ssrContext) || // stateful 123 | (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional 124 | // 2.2 with runInNewContext: true 125 | if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') { 126 | context = __VUE_SSR_CONTEXT__ 127 | } 128 | // inject component styles 129 | if (injectStyles) { 130 | injectStyles.call(this, context) 131 | } 132 | // register component module identifier for async chunk inferrence 133 | if (context && context._registeredComponents) { 134 | context._registeredComponents.add(moduleIdentifier) 135 | } 136 | } 137 | // used by ssr in case component is cached and beforeCreate 138 | // never gets called 139 | options._ssrRegister = hook 140 | } else if (injectStyles) { 141 | hook = injectStyles 142 | } 143 | 144 | if (hook) { 145 | var functional = options.functional 146 | var existing = functional 147 | ? options.render 148 | : options.beforeCreate 149 | 150 | if (!functional) { 151 | // inject component registration as beforeCreate hook 152 | options.beforeCreate = existing 153 | ? [].concat(existing, hook) 154 | : [hook] 155 | } else { 156 | // for template-only hot-reload because in that case the render fn doesn't 157 | // go through the normalizer 158 | options._injectStyles = hook 159 | // register for functioal component in vue file 160 | options.render = function renderWithStyleInjection (h, context) { 161 | hook.call(context) 162 | return existing(h, context) 163 | } 164 | } 165 | } 166 | 167 | return { 168 | esModule: esModule, 169 | exports: scriptExports, 170 | options: options 171 | } 172 | } 173 | 174 | 175 | /***/ }), 176 | /* 1 */ 177 | /***/ (function(module, exports) { 178 | 179 | /* 180 | MIT License http://www.opensource.org/licenses/mit-license.php 181 | Author Tobias Koppers @sokra 182 | */ 183 | // css base code, injected by the css-loader 184 | module.exports = function(useSourceMap) { 185 | var list = []; 186 | 187 | // return the list of modules as css string 188 | list.toString = function toString() { 189 | return this.map(function (item) { 190 | var content = cssWithMappingToString(item, useSourceMap); 191 | if(item[2]) { 192 | return "@media " + item[2] + "{" + content + "}"; 193 | } else { 194 | return content; 195 | } 196 | }).join(""); 197 | }; 198 | 199 | // import a list of modules into the list 200 | list.i = function(modules, mediaQuery) { 201 | if(typeof modules === "string") 202 | modules = [[null, modules, ""]]; 203 | var alreadyImportedModules = {}; 204 | for(var i = 0; i < this.length; i++) { 205 | var id = this[i][0]; 206 | if(typeof id === "number") 207 | alreadyImportedModules[id] = true; 208 | } 209 | for(i = 0; i < modules.length; i++) { 210 | var item = modules[i]; 211 | // skip already imported module 212 | // this implementation is not 100% perfect for weird media query combinations 213 | // when a module is imported multiple times with different media queries. 214 | // I hope this will never occur (Hey this way we have smaller bundles) 215 | if(typeof item[0] !== "number" || !alreadyImportedModules[item[0]]) { 216 | if(mediaQuery && !item[2]) { 217 | item[2] = mediaQuery; 218 | } else if(mediaQuery) { 219 | item[2] = "(" + item[2] + ") and (" + mediaQuery + ")"; 220 | } 221 | list.push(item); 222 | } 223 | } 224 | }; 225 | return list; 226 | }; 227 | 228 | function cssWithMappingToString(item, useSourceMap) { 229 | var content = item[1] || ''; 230 | var cssMapping = item[3]; 231 | if (!cssMapping) { 232 | return content; 233 | } 234 | 235 | if (useSourceMap && typeof btoa === 'function') { 236 | var sourceMapping = toComment(cssMapping); 237 | var sourceURLs = cssMapping.sources.map(function (source) { 238 | return '/*# sourceURL=' + cssMapping.sourceRoot + source + ' */' 239 | }); 240 | 241 | return [content].concat(sourceURLs).concat([sourceMapping]).join('\n'); 242 | } 243 | 244 | return [content].join('\n'); 245 | } 246 | 247 | // Adapted from convert-source-map (MIT) 248 | function toComment(sourceMap) { 249 | // eslint-disable-next-line no-undef 250 | var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))); 251 | var data = 'sourceMappingURL=data:application/json;charset=utf-8;base64,' + base64; 252 | 253 | return '/*# ' + data + ' */'; 254 | } 255 | 256 | 257 | /***/ }), 258 | /* 2 */ 259 | /***/ (function(module, exports, __webpack_require__) { 260 | 261 | module.exports = __webpack_require__(3); 262 | 263 | 264 | /***/ }), 265 | /* 3 */ 266 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 267 | 268 | "use strict"; 269 | Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); 270 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__components_DatePicker__ = __webpack_require__(4); 271 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__components_DatePicker___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__components_DatePicker__); 272 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__components_FilterSelector__ = __webpack_require__(16); 273 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__components_FilterSelector___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1__components_FilterSelector__); 274 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__components_SelectFilter__ = __webpack_require__(19); 275 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__components_SelectFilter___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_2__components_SelectFilter__); 276 | 277 | 278 | 279 | 280 | Nova.booting(function (Vue, router) { 281 | Vue.component('date-picker', __WEBPACK_IMPORTED_MODULE_0__components_DatePicker___default.a); 282 | Vue.component('filter-selector', __WEBPACK_IMPORTED_MODULE_1__components_FilterSelector___default.a); 283 | Vue.component('select-filter', __WEBPACK_IMPORTED_MODULE_2__components_SelectFilter___default.a); 284 | }); 285 | 286 | /***/ }), 287 | /* 4 */ 288 | /***/ (function(module, exports, __webpack_require__) { 289 | 290 | var disposed = false 291 | function injectStyle (ssrContext) { 292 | if (disposed) return 293 | __webpack_require__(5) 294 | } 295 | var normalizeComponent = __webpack_require__(0) 296 | /* script */ 297 | var __vue_script__ = __webpack_require__(9) 298 | /* template */ 299 | var __vue_template__ = __webpack_require__(15) 300 | /* template functional */ 301 | var __vue_template_functional__ = false 302 | /* styles */ 303 | var __vue_styles__ = injectStyle 304 | /* scopeId */ 305 | var __vue_scopeId__ = "data-v-fa816ef2" 306 | /* moduleIdentifier (server only) */ 307 | var __vue_module_identifier__ = null 308 | var Component = normalizeComponent( 309 | __vue_script__, 310 | __vue_template__, 311 | __vue_template_functional__, 312 | __vue_styles__, 313 | __vue_scopeId__, 314 | __vue_module_identifier__ 315 | ) 316 | Component.options.__file = "resources/js/components/DatePicker.vue" 317 | 318 | /* hot reload */ 319 | if (false) {(function () { 320 | var hotAPI = require("vue-hot-reload-api") 321 | hotAPI.install(require("vue"), false) 322 | if (!hotAPI.compatible) return 323 | module.hot.accept() 324 | if (!module.hot.data) { 325 | hotAPI.createRecord("data-v-fa816ef2", Component.options) 326 | } else { 327 | hotAPI.reload("data-v-fa816ef2", Component.options) 328 | } 329 | module.hot.dispose(function (data) { 330 | disposed = true 331 | }) 332 | })()} 333 | 334 | module.exports = Component.exports 335 | 336 | 337 | /***/ }), 338 | /* 5 */ 339 | /***/ (function(module, exports, __webpack_require__) { 340 | 341 | // style-loader: Adds some css to the DOM by adding a 95 | -------------------------------------------------------------------------------- /resources/js/components/FilterSelector.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 68 | -------------------------------------------------------------------------------- /resources/js/components/SelectFilter.vue: -------------------------------------------------------------------------------- 1 | 18 | 38 | -------------------------------------------------------------------------------- /resources/js/index.js: -------------------------------------------------------------------------------- 1 | import DatePicker from './components/DatePicker' 2 | import FilterSelector from './components/FilterSelector' 3 | import SelectFilter from './components/SelectFilter' 4 | 5 | Nova.booting((Vue, router) => { 6 | Vue.component('date-picker', DatePicker) 7 | Vue.component('filter-selector', FilterSelector) 8 | Vue.component('select-filter', SelectFilter) 9 | }) 10 | -------------------------------------------------------------------------------- /src/CustomFilter.php: -------------------------------------------------------------------------------- 1 | true, 18 | 'component' => $this->componentName(), 19 | ]); 20 | } 21 | 22 | /** 23 | * The name of the Vue component to be used for this filter 24 | * 25 | * @return string 26 | */ 27 | protected abstract function componentName(); 28 | } 29 | -------------------------------------------------------------------------------- /src/DateFilter.php: -------------------------------------------------------------------------------- 1 |