├── .editorconfig ├── .env ├── .github ├── FUNDING.yml └── test.yml ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── __tests__ ├── App.test.js ├── DataTable.test.js ├── DataTableCell.test.js ├── DataTableFilters.test.js ├── DataTableHeader.test.js ├── Table.test.js └── bootstrap.js ├── babel.config.js ├── docs ├── 404.html ├── css │ ├── app.4397df0e.css │ └── chunk-vendors.872b69a4.css ├── index.html └── js │ ├── app.4a34b8cf.js │ ├── app.4a34b8cf.js.map │ ├── chunk-vendors.50daf35e.js │ └── chunk-vendors.50daf35e.js.map ├── documentation ├── App.vue ├── assets │ └── styles │ │ └── main.css ├── components │ ├── Header.vue │ └── Navbar.vue ├── example-components │ ├── DataTableAnchorCell.vue │ ├── DataTableButtonCell.vue │ ├── DataTableCurrencyCell.vue │ ├── DataTableDateCell.vue │ ├── DataTableDropdownCell.vue │ ├── DataTableImageCell.vue │ ├── DataTableIsActiveCell.vue │ ├── DataTableListCell.vue │ ├── DataTableNameAndImageCell.vue │ ├── DataTableSelectCell.vue │ ├── InformationAlert.vue │ ├── Modal.vue │ ├── ModalButton.vue │ └── Switch.vue ├── filters │ ├── format-date.ts │ └── pad-digit.ts ├── main.ts ├── markdown │ ├── events │ │ └── table.md │ ├── examples │ │ ├── basic.md │ │ ├── crud.md │ │ ├── custom-filters.md │ │ ├── injecting-dynamic-components.md │ │ ├── joins-js.md │ │ ├── joins-php.md │ │ ├── loading-animations.md │ │ ├── modal.md │ │ ├── override-filters-and-pagination.md │ │ ├── override-table-body.md │ │ ├── override-table-header.md │ │ ├── own-data.md │ │ ├── relationships │ │ │ ├── belongs-to-js.md │ │ │ ├── belongs-to-many-js.md │ │ │ ├── belongs-to-many-php.md │ │ │ ├── belongs-to-php.md │ │ │ ├── has-many-js.md │ │ │ ├── has-many-php.md │ │ │ ├── introduction.md │ │ │ └── mysql-config.md │ │ ├── reloading-the-table.md │ │ ├── styling.md │ │ └── tailwind.md │ ├── install.md │ ├── introduction.md │ ├── laravel │ │ ├── adding-trait.md │ │ ├── controller-resource.md │ │ ├── install.md │ │ └── options.md │ └── props │ │ ├── cell.md │ │ ├── column.md │ │ └── table.md ├── mixins │ ├── CodeExample.js │ ├── CorrectTableStyling.js │ └── CorrectTextStyling.js ├── mutations.ts ├── routes.ts ├── state.ts └── views │ ├── Install.vue │ ├── Introduction.vue │ ├── Soon.vue │ ├── events │ └── Table.vue │ ├── examples │ ├── Basic.vue │ ├── CRUD.vue │ ├── CustomFilters.vue │ ├── InjectingDynamicComponents.vue │ ├── Joins.vue │ ├── LoadingAnimations.vue │ ├── Modal.vue │ ├── OverrideFiltersAndPagination.vue │ ├── OverrideTableBody.vue │ ├── OverrideTableHeader.vue │ ├── ReloadingTheTable.vue │ ├── Styling.vue │ ├── Tailwind.vue │ ├── UsingYourOwnData.vue │ └── relationships │ │ ├── BelongsTo.vue │ │ ├── BelongsToMany.vue │ │ ├── HasMany.vue │ │ └── Introduction.vue │ ├── laravel │ ├── AddingTrait.vue │ ├── ControllerResource.vue │ ├── Install.vue │ └── Options.vue │ └── props │ ├── Cell.vue │ ├── Column.vue │ └── Table.vue ├── editor.conf ├── jest.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── 404.html └── index.html ├── src ├── components │ ├── DataTable.vue │ ├── DataTableCell.vue │ ├── DataTableFilters.vue │ ├── DataTableTh.vue │ └── Table.vue ├── functions │ └── MergeClasses.ts ├── mixins │ └── UrlFilters.js ├── plugin.ts ├── shims-tsx.d.ts ├── shims-vue.d.ts ├── themes │ ├── Bootstrap.ts │ └── Tailwind.ts └── validators │ ├── data-table-framework.ts │ ├── data-table-order-dir.ts │ └── data-table-theme.ts ├── tailwind.config.js ├── tsconfig.json └── vue.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | # Unix-style newlines with a newline ending every file 5 | [*] 6 | end_of_line = lf 7 | insert_final_newline = true 8 | 9 | # Matches multiple files with brace expansion notation 10 | # Set default charset 11 | [*.{js,py}] 12 | charset = utf-8 13 | 14 | # 4 space indentation 15 | [*.js,*.vue ] 16 | indent_style = space 17 | indent_size = 4 18 | 19 | # Tab indentation (no size specified) 20 | [Makefile] 21 | indent_style = tab 22 | 23 | # Indentation override for all JS under lib directory 24 | [lib/**.js] 25 | indent_style = space 26 | indent_size = 4 27 | 28 | # Matches the exact files either package.json or .travis.yml 29 | [{package.json,.travis.yml}] 30 | indent_style = space 31 | indent_size = 2 -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | VUE_APP_PIVOT_URL= 2 | VUE_APP_ELOQUENT_URL= 3 | VUE_APP_QUERY_BUILDER_URL= 4 | VUE_APP_DATATABLE_URL_SEARCH= -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [jamesdordoy] -------------------------------------------------------------------------------- /.github/test.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Vue Test Utils - Jest 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [14.x] 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Use Node.js ${{ matrix.node-version }} 24 | uses: actions/setup-node@v1 25 | with: 26 | node-version: ${{ matrix.node-version }} 27 | - run: npm ci 28 | - run: npm run build --if-present 29 | - run: npm run test -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # prod env files 10 | .env.production 11 | .env.*.production 12 | 13 | # Log files 14 | npm-debug.log* 15 | yarn-debug.log* 16 | yarn-error.log* 17 | 18 | # Editor directories and files 19 | .idea 20 | .vscode 21 | *.suo 22 | *.ntvs* 23 | *.njsproj 24 | *.sln 25 | *.sw? 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - stable 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Release Notes 2 | 3 | ## [v0.6.0 (2020-10-04)] 4 | 5 | 6 | ### Changed 7 | - Updated the slot-scope name for tableData to tableFilters. the slot-scope for tableData now reflects the table data 8 | 9 | 10 | ## [v0.3.0 (2019-07-23)] 11 | 12 | 13 | ### Changed 14 | - Updated the event system to allow for different events to be passed through. Changed column prop `click-event` to `event` and `handler` ([#2f28d66](https://github.com/jamesdordoy/vue-datatable/commit/2f28d6646b4ed3c980d79d81f7c131c106a1aecd)) 15 | 16 | 17 | ## [v0.3.1 (2019-08-12)] 18 | 19 | 20 | ### Changed 21 | - Added new column attribute `meta`. This allows custom values, functions etc. to be parsed to the custom components ([#2570f00](https://github.com/jamesdordoy/vue-datatable/commit/2570f0004f17cb1d1f220f956c90be4b7f6217c3)) 22 | 23 | ## [v0.4.0 (2019-11-21)] 24 | 25 | 26 | ### Changed 27 | - Changed the model trait method in the Laravel Package. Changed method name `dataTableQuery` to `eloquentQuery` as there is now an additional method for using the Laravel Query Builder. ([#2570f00](https://github.com/jamesdordoy/vue-datatable/commit/2570f0004f17cb1d1f220f956c90be4b7f6217c3)) 28 | 29 | ## [v0.5.0 (2019-12-26)] 30 | 31 | 32 | ### Changed 33 | - Changed the filterable column prop in the table to orderable as it is a more appropriate name. ([#a521a8](https://github.com/jamesdordoy/vue-datatable/commit/a521a86a4c17dba809a9d26c3d845f5c5b1cae31)) 34 | 35 | ## [v0.6.0 (2020-01-23)] 36 | 37 | ### Changed 38 | - Changed table cell comp prop to component. ([#a521a8](https://github.com/jamesdordoy/vue-datatable/commit/a521a86a4c17dba809a9d26c3d845f5c5b1cae31)) 39 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 James Dordoy 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/jamesdordoy/Laravel-Vue-Datatable.svg?branch=master)](https://travis-ci.org/jamesdordoy/Laravel-Vue-Datatable) [![npm](https://img.shields.io/npm/v/laravel-vue-datatable.svg)](https://www.npmjs.com/package/laravel-vue-datatable) [![Downloads](https://img.shields.io/npm/dt/laravel-vue-datatable.svg)](https://www.npmjs.com/package/laravel-vue-datatable) 2 | 3 | # Laravel Vue Datatable 4 | A Vue.js datatable component for Laravel that works with Bootstrap & Tailwind by default. 5 | 6 | ## Requirements 7 | 8 | * [Vue.js](https://vuejs.org/) 2.x 9 | * [Laravel](http://laravel.com/docs/) 5.x 10 | * [Bootstrap](http://getbootstrap.com/) 4 (Optional) 11 | * [Tailwind](https://tailwindcss.com/) 1.* (Optional) 12 | 13 | ## Demo & Docs 14 | 15 | See [https://jamesdordoy.github.io/laravel-vue-datatable/](https://jamesdordoy.github.io/laravel-vue-datatable/) 16 | 17 | ## Example 18 | ![Image description](https://www.jamesdordoy.co.uk/images/projects/bootstrap-datatable.png?a=a) 19 | 20 | ## Laravel Package Installation 21 | See details [https://github.com/jamesdordoy/Laravel-Vue-Datatable_Laravel-Package](https://github.com/jamesdordoy/Laravel-Vue-Datatable_Laravel-Package) 22 | 23 | ## Component Installation 24 | 25 | ```bash 26 | npm install laravel-vue-datatable 27 | 28 | or 29 | 30 | yarn add laravel-vue-datatable 31 | ``` 32 | 33 | ### Register the Plugin 34 | 35 | ```javascript 36 | import DataTable from 'laravel-vue-datatable'; 37 | 38 | Vue.use(DataTable); 39 | ``` 40 | 41 | ### Basic Example 42 | > UserDatatable.vue 43 | 44 | 45 | ```html 46 | 49 | 50 | ``` 51 | 52 | ```javascript 53 | export default { 54 | data() { 55 | return { 56 | columns: [ 57 | { 58 | label: 'ID', 59 | name: 'id', 60 | orderable: true, 61 | }, 62 | { 63 | label: 'Name', 64 | name: 'name', 65 | orderable: true, 66 | }, 67 | { 68 | label: 'Email', 69 | name: 'email', 70 | orderable: true, 71 | }, 72 | ] 73 | } 74 | }, 75 | } 76 | ``` 77 | 78 | ### API 79 | 80 | #### Datatable Props 81 | 82 | | Name | Type | Default | Description 83 | | --- | --- | --- | --- | 84 | | `url ` | String | "/" | The JSON url | 85 | | `columns` | Array | [] | The table columns | 86 | | `order-by` | String | "id" | (optional) The default column to order your data by | 87 | | `order-dir` | String | "asc" | (optional) The default order by direction | 88 | | `per-page` | Array | ['10','25','50'] | (optional) Amount to be displayed | 89 | | `theme` | String | "light" | (optional) Must be dark or light | 90 | | `framework` | String | "bootstrap" | (optional) Must be bootstrap or tailwind | 91 | | `debounce-delay` | Number | 0 | (optional) Adds a debounce delay to the get request when searching | 92 | | `classes` | Object | See Below | (optional) Table classes | 93 | | `translate` | Object | { nextButton: 'Next', previousButton: 'Previous', placeholderSearch: 'Search Table'} | (optional) used to overwrite the default pagination button text and search input placeholder | 94 | | `pagination` | Object | {} | (optional) props for [tailable/pagination](https://github.com/tailable/pagination#pagination-props) | 95 | | `add-filters-to-url` | Boolean | false | (optional) Will adjust the current url to keep track of used filters and will also store them in local storage. | 96 | | `headers` | Object | {} | Additional headers to pass route e.g. bearer token | 97 | 98 | #### Default Table Classes 99 | 100 | ```json 101 | { 102 | "table-container": { 103 | "table-responsive": true, 104 | }, 105 | "table": { 106 | "table": true, 107 | "table-striped": true, 108 | "table-dark": true, 109 | }, 110 | "t-head": { 111 | 112 | }, 113 | "t-body": { 114 | 115 | }, 116 | "t-head-tr": { 117 | 118 | }, 119 | "t-body-tr": { 120 | 121 | }, 122 | "td": { 123 | 124 | }, 125 | "th": { 126 | 127 | }, 128 | } 129 | ``` 130 | 131 | ### Column Props 132 | | Name | Type | Default | Description 133 | | --- | --- | --- | --- | 134 | | `label` | String | "" | The table column label to be displayed as the column heading | 135 | | `name` | String | "" | The table column header name. You can also access nested properties e.g. a query using a with relationship using the dot notation. | 136 | | `columnName` | String | "" | (optional) The backend column name if the provided data keys do not match with the backend database. It may also be required to prefix the column name with the table name e.g. users.name to avoid issues with Integrity constraint violation when joining tables | 137 | | `width` | Number | 0 | (optional) The table column width | 138 | | `orderable` | Boolean | false | (optional) Is the column orderable in the datatable | 139 | | `component` | Component | null | (optional) A dynamic component that can be injected | 140 | | `event` | String | "" | (optional) Event type to parse to the component e.g. click, focus etc. | 141 | | `handler` | Function | () => {} | (optional) Callback function to parse for the event handler | 142 | | `transform` | Function | () => {} | (optional) Callback function to parse to manipulate the result. e.g. add currency symbol | 143 | | `classes` | Object | {} | (optional) Component classes to parse | 144 | | `meta` | Object | {} | (optional) Additional values that are parsed to component | 145 | 146 | 147 | ## Reloading the table manually 148 | 149 | If updates have been made to your dataset and you need to reload the table, you can attach a [ref](https://vuejs.org/v2/api/#vm-refs) to the table. Once the Vue.JS reference is attached, you are able to access the underlining methods of the component including the getData method. 150 | 151 | Alternatively, if you have custom filters applied and you would prefered they are retained, any adjustment to the url the table uses as a prop will reload the table. 152 | 153 | 154 | ## Further Examples 155 | 156 | See [https://jamesdordoy.github.io/laravel-vue-datatable/](https://jamesdordoy.github.io/laravel-vue-datatable/) for more examples. 157 | 158 | ## Development 159 | 160 | To work on the package locally or to add to the documentation, run the following command: 161 | 162 | ```bash 163 | npm run serve 164 | ``` 165 | 166 | To deploy documentation to GitHub under a PR. Please run the following after uncommenting the outputDir line in the vue.config.js file: 167 | 168 | ```bash 169 | npm run build-docs 170 | ``` 171 | 172 | To run the tests: 173 | 174 | ```bash 175 | npm run test 176 | ``` -------------------------------------------------------------------------------- /__tests__/App.test.js: -------------------------------------------------------------------------------- 1 | //Imports 2 | import App from '../documentation/App' 3 | import { shallowMount } from '@vue/test-utils' 4 | 5 | describe('Laravel Vue Data Tables Demo', () => { 6 | test('can be mounted cleanly', () => { 7 | const wrapper = shallowMount(App); 8 | expect(wrapper.isVueInstance()).toBeTruthy(); 9 | }); 10 | }); -------------------------------------------------------------------------------- /__tests__/DataTable.test.js: -------------------------------------------------------------------------------- 1 | //Imports 2 | import { shallowMount } from '@vue/test-utils' 3 | import DataTable from '../src/components/DataTable' 4 | 5 | describe('Data Table', () => { 6 | test('can be mounted cleanly', () => { 7 | const wrapper = shallowMount(DataTable, { 8 | propsData: { 9 | columns: [ 10 | { 11 | label: 'ID', 12 | name: 'id', 13 | orderable: true, 14 | width: 10, 15 | }, 16 | { 17 | label: 'Name', 18 | name: 'name', 19 | orderable: true, 20 | width: 20, 21 | }, 22 | ], 23 | url: '', 24 | } 25 | }); 26 | 27 | expect(wrapper.isVueInstance()).toBeTruthy(); 28 | }); 29 | }); -------------------------------------------------------------------------------- /__tests__/DataTableCell.test.js: -------------------------------------------------------------------------------- 1 | //Imports 2 | import { shallowMount } from '@vue/test-utils' 3 | import DataTableCell from '../src/components/DataTableCell' 4 | 5 | describe('Data Table Cell', () => { 6 | test('can be mounted cleanly', () => { 7 | const wrapper = shallowMount(DataTableCell, { 8 | propsData: { 9 | value:{ 10 | name: 'test', 11 | }, 12 | name: 'name' 13 | } 14 | }); 15 | 16 | expect(wrapper.isVueInstance()).toBeTruthy(); 17 | }); 18 | 19 | test('can be mounted with correct prop values', () => { 20 | const wrapper = shallowMount(DataTableCell, { 21 | propsData: { 22 | value:{ 23 | name: 'test', 24 | }, 25 | name: 'name' 26 | } 27 | }); 28 | 29 | expect(wrapper.props().value.name).toBe('test'); 30 | expect(wrapper.props().name).toBe('name'); 31 | }); 32 | 33 | test('can be mounted with a default value', () => { 34 | const wrapper = shallowMount(DataTableCell, { 35 | propsData: { 36 | value:{ 37 | name: 'test', 38 | }, 39 | name: 'name' 40 | } 41 | }); 42 | 43 | expect(wrapper.text()).toBe('test'); 44 | }); 45 | 46 | test('can tranform values', () => { 47 | const wrapper = shallowMount(DataTableCell, { 48 | propsData: { 49 | value:{ 50 | name: 'james', 51 | }, 52 | name: 'name', 53 | transform: ({data, name}) => { return data[name].charAt(0).toUpperCase() + data[name].slice(1) } 54 | } 55 | }); 56 | 57 | expect(wrapper.text()).toBe('James'); 58 | }); 59 | }); -------------------------------------------------------------------------------- /__tests__/DataTableFilters.test.js: -------------------------------------------------------------------------------- 1 | //Imports 2 | import { shallowMount } from '@vue/test-utils' 3 | import DataTableFilters from '../src/components/DataTableFilters' 4 | 5 | describe('Data Table Filters', () => { 6 | test('can be mounted cleanly', () => { 7 | const wrapper = shallowMount(DataTableFilters, { 8 | propsData: { 9 | tableData: { 10 | filters: {}, 11 | }, 12 | perPage: [], 13 | }, 14 | }); 15 | 16 | expect(wrapper.isVueInstance()).toBeTruthy(); 17 | }); 18 | }); -------------------------------------------------------------------------------- /__tests__/DataTableHeader.test.js: -------------------------------------------------------------------------------- 1 | //Imports 2 | import { shallowMount } from '@vue/test-utils' 3 | import DataTableTh from '../src/components/DataTableTh' 4 | 5 | describe('Data Table Header', () => { 6 | test('can be mounted cleanly', () => { 7 | const wrapper = shallowMount(DataTableTh, { 8 | propsData: { 9 | column: { 10 | 11 | }, 12 | dir: 'thing', 13 | }, 14 | }); 15 | 16 | expect(wrapper.isVueInstance()).toBeTruthy(); 17 | }); 18 | }); -------------------------------------------------------------------------------- /__tests__/Table.test.js: -------------------------------------------------------------------------------- 1 | //Imports 2 | import Table from '../src/components/Table' 3 | import { shallowMount } from '@vue/test-utils' 4 | 5 | 6 | describe('Table', () => { 7 | test('can be mounted cleanly', () => { 8 | const wrapper = shallowMount(Table, { 9 | propsData: { 10 | columns: [], 11 | url: '', 12 | }, 13 | }); 14 | 15 | expect(wrapper.isVueInstance()).toBeTruthy(); 16 | }); 17 | }); -------------------------------------------------------------------------------- /__tests__/bootstrap.js: -------------------------------------------------------------------------------- 1 | // Attach Vue 2 | global.Vue = require('vue'); 3 | 4 | import VueRouter from 'vue-router'; 5 | 6 | //Attach an alert function to the window as JSDOM dosnt do this but our components makes use of the function 7 | //https://stackoverflow.com/questions/55088482/jest-not-implemented-window-alert 8 | window.alert = () => {}; 9 | 10 | //Ignore log and error console messages as exceptions are classed under these. 11 | global.console = { 12 | log: jest.fn(), 13 | error: jest.fn(), 14 | warn: console.warn, 15 | info: console.info, 16 | debug: console.debug, 17 | }; 18 | 19 | Vue.use(VueRouter); 20 | 21 | import { TailablePagination } from 'tailable-pagination'; 22 | 23 | Vue.component('tailable-pagination', TailablePagination); -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ], 5 | sourceType: 'unambiguous' 6 | } 7 | 8 | -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Single Page Apps for GitHub Pages 6 | 7 | 8 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /docs/css/app.4397df0e.css: -------------------------------------------------------------------------------- 1 | @import url(https://fonts.googleapis.com/css?family=Oxanium&display=swap);@import url(https://fonts.googleapis.com/css?family=Varela+Round&display=swap);.show-code-inline{position:absolute;bottom:8px;right:18px}.switch[data-v-3365f90a]{position:relative;display:inline-block;width:46px;height:20px;margin-bottom:0}.switch input[data-v-3365f90a]{opacity:0;width:0;height:0}.slider[data-v-3365f90a]{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background-color:#ccc;-webkit-transition:.4s;transition:.4s}.slider[data-v-3365f90a]:before{position:absolute;content:"";height:12px;width:12px;left:4px;bottom:4px;background-color:#fff;-webkit-transition:.4s;transition:.4s}input:checked+.slider[data-v-3365f90a]{background-color:#2196f3}input:focus+.slider[data-v-3365f90a]{-webkit-box-shadow:0 0 1px #2196f3;box-shadow:0 0 1px #2196f3}input:checked+.slider[data-v-3365f90a]:before{-webkit-transform:translateX(26px);transform:translateX(26px)}.slider.round[data-v-3365f90a]{border-radius:34px}.slider.round[data-v-3365f90a]:before{border-radius:50%}li[data-v-df9fa13e]{padding:4px;margin:0}ul[data-v-df9fa13e]{margin:0;padding:0}a[data-v-2ccc1c8d]:hover{color:#4dc0b5}a[data-v-2ccc1c8d]{color:#3490dc}.vs__search{padding:2px!important} 2 | 3 | /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}b,strong{font-weight:bolder}code{font-family:monospace,monospace;font-size:1em}img{border-style:none}button,input,select{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}[type=checkbox]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}[hidden],template{display:none}h2,h5,p,pre{margin:0}button{background-color:transparent;background-image:none}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}ul{list-style:none;margin:0;padding:0}html{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}*,:after,:before{-webkit-box-sizing:border-box;box-sizing:border-box;border-width:0;border-color:#e2e8f0}*,:after,:before,img{border-style:solid}input::-webkit-input-placeholder{color:#a0aec0}input::-moz-placeholder{color:#a0aec0}input:-ms-input-placeholder{color:#a0aec0}input::-ms-input-placeholder{color:#a0aec0}input::placeholder{color:#a0aec0}[role=button],button{cursor:pointer}table{border-collapse:collapse}h2,h5{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}button,input,select{padding:0;line-height:inherit;color:inherit}code,pre{font-family:Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}img,svg{display:block;vertical-align:middle}img{max-width:100%;height:auto}.bg-gray-100{--bg-opacity:1;background-color:#f7fafc;background-color:rgba(247,250,252,var(--bg-opacity))}.bg-gray-400{--bg-opacity:1;background-color:#cbd5e0;background-color:rgba(203,213,224,var(--bg-opacity))}.bg-gray-500{--bg-opacity:1;background-color:#a0aec0;background-color:rgba(160,174,192,var(--bg-opacity))}.bg-red-200{--bg-opacity:1;background-color:#fed7d7;background-color:rgba(254,215,215,var(--bg-opacity))}.bg-green-200{--bg-opacity:1;background-color:#c6f6d5;background-color:rgba(198,246,213,var(--bg-opacity))}.bg-blue-800{--bg-opacity:1;background-color:#2c5282;background-color:rgba(44,82,130,var(--bg-opacity))}.hover\:bg-gray-300:hover{--bg-opacity:1;background-color:#e2e8f0;background-color:rgba(226,232,240,var(--bg-opacity))}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.border-r{border-right-width:1px}.cursor-pointer{cursor:pointer}.block{display:block}.inline-block{display:inline-block}.flex{display:-webkit-box;display:-ms-flexbox;display:flex}.table{display:table}.hidden{display:none}.flex-wrap{-ms-flex-wrap:wrap;flex-wrap:wrap}.flex-shrink-0{-ms-flex-negative:0;flex-shrink:0}.float-right{float:right}.font-semibold{font-weight:600}.h-6{height:1.5rem}.h-10{height:2.5rem}.h-full{height:100%}.h-screen{height:100vh}.text-xs{font-size:.75rem}.text-sm{font-size:.875rem}.text-lg{font-size:1.125rem}.m-0{margin:0}.mx-auto{margin-left:auto;margin-right:auto}.-mx-4{margin-left:-1rem;margin-right:-1rem}.mt-0{margin-top:0}.mb-2{margin-bottom:.5rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.min-h-full{min-height:100%}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.p-0{padding:0}.p-2{padding:.5rem}.p-3{padding:.75rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.px-4{padding-left:1rem;padding-right:1rem}.pl-2{padding-left:.5rem}.pr-3{padding-right:.75rem}.pl-6{padding-left:1.5rem}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.top-0{top:0}.shadow{-webkit-box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06);box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06)}.fill-current{fill:currentColor}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-white{--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity))}.text-gray-500{--text-opacity:1;color:#a0aec0;color:rgba(160,174,192,var(--text-opacity))}.text-gray-600{--text-opacity:1;color:#718096;color:rgba(113,128,150,var(--text-opacity))}.text-gray-800{--text-opacity:1;color:#2d3748;color:rgba(45,55,72,var(--text-opacity))}.text-gray-900{--text-opacity:1;color:#1a202c;color:rgba(26,32,44,var(--text-opacity))}.text-teal-800{--text-opacity:1;color:#285e61;color:rgba(40,94,97,var(--text-opacity))}.hover\:text-gray-700:hover{--text-opacity:1;color:#4a5568;color:rgba(74,85,104,var(--text-opacity))}.uppercase{text-transform:uppercase}.tracking-wide{letter-spacing:.025em}.whitespace-no-wrap{white-space:nowrap}.w-6{width:1.5rem}.w-10{width:2.5rem}.w-1\/3{width:33.333333%}.w-2\/3{width:66.666667%}.w-full{width:100%}.z-10{z-index:10}.transform{--transform-translate-x:0;--transform-translate-y:0;--transform-rotate:0;--transform-skew-x:0;--transform-skew-y:0;--transform-scale-x:1;--transform-scale-y:1;-webkit-transform:translateX(var(--transform-translate-x)) translateY(var(--transform-translate-y)) rotate(var(--transform-rotate)) skewX(var(--transform-skew-x)) skewY(var(--transform-skew-y)) scaleX(var(--transform-scale-x)) scaleY(var(--transform-scale-y));transform:translateX(var(--transform-translate-x)) translateY(var(--transform-translate-y)) rotate(var(--transform-rotate)) skewX(var(--transform-skew-x)) skewY(var(--transform-skew-y)) scaleX(var(--transform-scale-x)) scaleY(var(--transform-scale-y))}.transition{-webkit-transition-property:background-color,border-color,color,fill,stroke,opacity,-webkit-box-shadow,-webkit-transform;transition-property:background-color,border-color,color,fill,stroke,opacity,-webkit-box-shadow,-webkit-transform;transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform;transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform,-webkit-box-shadow,-webkit-transform}@-webkit-keyframes spin{to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes spin{to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes ping{75%,to{-webkit-transform:scale(2);transform:scale(2);opacity:0}}@keyframes pulse{50%{opacity:.5}}@keyframes bounce{0%,to{-webkit-transform:translateY(-25%);transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{-webkit-transform:none;transform:none;-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}h2{font-size:1.875rem;margin-top:.5rem;margin-bottom:.5rem}.varela{font-family:Varela Round,sans-serif}.oxanium{font-family:Oxanium,cursive}body{padding-top:50px}.router-link-active{background:#cbd5e0!important;border:1px solid #ccc}.slideDown{-webkit-animation-name:slideDownAnimation;animation-name:slideDownAnimation;-webkit-animation-duration:1s;animation-duration:1s}.max-h-0{max-height:0}@-webkit-keyframes slideDownAnimation{0%{max-height:0%}to{max-height:auto}}@keyframes slideDownAnimation{0%{max-height:0%}to{max-height:auto}}.table{width:100%;max-width:100%;margin-bottom:1rem}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #eceeef}.table thead th{vertical-align:bottom;border-bottom:2px solid #eceeef}.table tbody+tbody{border-top:2px solid #eceeef}.table .table{background-color:#fff}@media (min-width:640px){.sm\:-mx-8{margin-left:-2rem;margin-right:-2rem}.sm\:px-8{padding-left:2rem;padding-right:2rem}}@media (min-width:768px){.md\:justify-start{-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}}@media (min-width:1024px){.lg\:block{display:block}.lg\:hidden{display:none}.lg\:text-2xl{font-size:1.5rem}.lg\:w-1\/6{width:16.666667%}.lg\:w-5\/6{width:83.333333%}} -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | Laravel Vue Datatable
-------------------------------------------------------------------------------- /documentation/App.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 25 | 26 | 38 | -------------------------------------------------------------------------------- /documentation/assets/styles/main.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @import url('https://fonts.googleapis.com/css?family=Oxanium&display=swap'); 6 | @import url('https://fonts.googleapis.com/css?family=Varela+Round&display=swap'); 7 | 8 | 9 | h1 { 10 | @apply text-4xl my-2; 11 | } 12 | 13 | h2 { 14 | @apply text-3xl my-2; 15 | } 16 | 17 | 18 | h3 { 19 | @apply text-2xl my-2; 20 | } 21 | 22 | .varela { 23 | font-family: 'Varela Round', sans-serif; 24 | } 25 | 26 | .oxanium { 27 | font-family: 'Oxanium', cursive; 28 | } 29 | 30 | body { 31 | padding-top: 50px; 32 | } 33 | 34 | .router-link-active{ 35 | background: #cbd5e0 !important; 36 | border: 1px solid #ccc; 37 | } 38 | 39 | .slideDown { 40 | animation-name: slideDownAnimation; 41 | animation-duration: 1s; 42 | } 43 | 44 | .max-h-0 { 45 | max-height: 0; 46 | } 47 | 48 | @keyframes slideDownAnimation { 49 | from { max-height: 0%; } 50 | to { max-height: auto; } 51 | } 52 | 53 | 54 | 55 | .table { 56 | width: 100%; 57 | max-width: 100%; 58 | margin-bottom: 1rem; 59 | } 60 | 61 | .table th, 62 | .table td { 63 | padding: 0.75rem; 64 | vertical-align: top; 65 | border-top: 1px solid #eceeef; 66 | } 67 | 68 | .table thead th { 69 | vertical-align: bottom; 70 | border-bottom: 2px solid #eceeef; 71 | } 72 | 73 | .table tbody + tbody { 74 | border-top: 2px solid #eceeef; 75 | } 76 | 77 | .table .table { 78 | background-color: #fff; 79 | } 80 | 81 | .table-sm th, 82 | .table-sm td { 83 | padding: 0.3rem; 84 | } 85 | 86 | .table-bordered { 87 | border: 1px solid #eceeef; 88 | } 89 | 90 | .table-bordered th, 91 | .table-bordered td { 92 | border: 1px solid #eceeef; 93 | } 94 | 95 | .table-bordered thead th, 96 | .table-bordered thead td { 97 | border-bottom-width: 2px; 98 | } 99 | 100 | .table-striped tbody tr:nth-of-type(odd) { 101 | background-color: rgba(0, 0, 0, 0.05); 102 | } 103 | 104 | .table-hover tbody tr:hover { 105 | background-color: rgba(0, 0, 0, 0.075); 106 | } 107 | 108 | .table-active, 109 | .table-active > th, 110 | .table-active > td { 111 | background-color: rgba(0, 0, 0, 0.075); 112 | } 113 | 114 | .table-hover .table-active:hover { 115 | background-color: rgba(0, 0, 0, 0.075); 116 | } 117 | 118 | .table-hover .table-active:hover > td, 119 | .table-hover .table-active:hover > th { 120 | background-color: rgba(0, 0, 0, 0.075); 121 | } 122 | 123 | .table-success, 124 | .table-success > th, 125 | .table-success > td { 126 | background-color: #dff0d8; 127 | } 128 | 129 | .table-hover .table-success:hover { 130 | background-color: #d0e9c6; 131 | } 132 | 133 | .table-hover .table-success:hover > td, 134 | .table-hover .table-success:hover > th { 135 | background-color: #d0e9c6; 136 | } 137 | 138 | .table-info, 139 | .table-info > th, 140 | .table-info > td { 141 | background-color: #d9edf7; 142 | } 143 | 144 | .table-hover .table-info:hover { 145 | background-color: #c4e3f3; 146 | } 147 | 148 | .table-hover .table-info:hover > td, 149 | .table-hover .table-info:hover > th { 150 | background-color: #c4e3f3; 151 | } 152 | 153 | .table-warning, 154 | .table-warning > th, 155 | .table-warning > td { 156 | background-color: #fcf8e3; 157 | } 158 | 159 | .table-hover .table-warning:hover { 160 | background-color: #faf2cc; 161 | } 162 | 163 | .table-hover .table-warning:hover > td, 164 | .table-hover .table-warning:hover > th { 165 | background-color: #faf2cc; 166 | } 167 | 168 | .table-danger, 169 | .table-danger > th, 170 | .table-danger > td { 171 | background-color: #f2dede; 172 | } 173 | 174 | .table-hover .table-danger:hover { 175 | background-color: #ebcccc; 176 | } 177 | 178 | .table-hover .table-danger:hover > td, 179 | .table-hover .table-danger:hover > th { 180 | background-color: #ebcccc; 181 | } 182 | 183 | .thead-inverse th { 184 | color: #fff; 185 | background-color: #292b2c; 186 | } 187 | 188 | .thead-default th { 189 | color: #464a4c; 190 | background-color: #eceeef; 191 | } 192 | 193 | .table-inverse { 194 | color: #fff; 195 | background-color: #292b2c; 196 | } 197 | 198 | .table-inverse th, 199 | .table-inverse td, 200 | .table-inverse thead th { 201 | border-color: #fff; 202 | } 203 | 204 | .table-inverse.table-bordered { 205 | border: 0; 206 | } 207 | 208 | .table-responsive { 209 | display: block; 210 | width: 100%; 211 | overflow-x: auto; 212 | -ms-overflow-style: -ms-autohiding-scrollbar; 213 | } 214 | 215 | .table-responsive.table-bordered { 216 | border: 0; 217 | } 218 | 219 | .table-dark { 220 | color: #fff; 221 | background-color: #343a40; 222 | } -------------------------------------------------------------------------------- /documentation/components/Header.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | -------------------------------------------------------------------------------- /documentation/example-components/DataTableAnchorCell.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | 16 | -------------------------------------------------------------------------------- /documentation/example-components/DataTableButtonCell.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | -------------------------------------------------------------------------------- /documentation/example-components/DataTableCurrencyCell.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | -------------------------------------------------------------------------------- /documentation/example-components/DataTableDateCell.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | 16 | -------------------------------------------------------------------------------- /documentation/example-components/DataTableDropdownCell.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | -------------------------------------------------------------------------------- /documentation/example-components/DataTableImageCell.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | 16 | -------------------------------------------------------------------------------- /documentation/example-components/DataTableIsActiveCell.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /documentation/example-components/DataTableListCell.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /documentation/example-components/DataTableNameAndImageCell.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | -------------------------------------------------------------------------------- /documentation/example-components/DataTableSelectCell.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 20 | 21 | -------------------------------------------------------------------------------- /documentation/example-components/InformationAlert.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /documentation/example-components/Modal.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | -------------------------------------------------------------------------------- /documentation/example-components/ModalButton.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | -------------------------------------------------------------------------------- /documentation/example-components/Switch.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 30 | 31 | -------------------------------------------------------------------------------- /documentation/filters/format-date.ts: -------------------------------------------------------------------------------- 1 | import moment from "moment"; 2 | 3 | export default (value, format='Do MMMM YYYY') => { 4 | if (value) { 5 | return moment(String(value)).format(format); 6 | } 7 | return 'N/A'; 8 | }; -------------------------------------------------------------------------------- /documentation/filters/pad-digit.ts: -------------------------------------------------------------------------------- 1 | export default (num, padlen, padchar) => { 2 | var pad_char = typeof padchar !== 'undefined' ? padchar : '0'; 3 | var pad = new Array(1 + padlen).join(pad_char); 4 | return (pad + num).slice(-pad.length); 5 | } -------------------------------------------------------------------------------- /documentation/main.ts: -------------------------------------------------------------------------------- 1 | //Imports 2 | import 'bootstrap'; 3 | import Vue from 'vue'; 4 | import Vuex from 'vuex'; 5 | import App from './App.vue'; 6 | import routes from './routes'; 7 | import VueRouter from 'vue-router'; 8 | import VueSweetalert2 from 'vue-sweetalert2'; 9 | import 'sweetalert2/dist/sweetalert2.min.css'; 10 | import createPersistedState from 'vuex-persistedstate'; 11 | import { library } from '@fortawesome/fontawesome-svg-core'; 12 | import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'; 13 | 14 | //Prism Code Highlighting 15 | import 'prismjs/components/prism-markup-templating.min.js'; 16 | import 'prismjs/components/prism-php.min.js'; 17 | import 'prismjs/components/prism-php-extras.min.js'; 18 | import 'prismjs/components/prism-bash.min.js'; 19 | import 'prismjs/components/prism-json.min.js'; 20 | 21 | import "prismjs/themes/prism-okaidia.css"; 22 | import "prismjs"; 23 | 24 | //Import Styles (Tailwind) 25 | import 'bootstrap/dist/css/bootstrap.css'; 26 | 27 | import './assets/styles/main.css'; 28 | 29 | //Import Plugin 30 | import DataTable from '../src/plugin'; 31 | 32 | Vue.use(VueSweetalert2); 33 | 34 | //Plugin Useage 35 | Vue.use(Vuex); 36 | Vue.use(DataTable); 37 | Vue.use(VueRouter); 38 | 39 | //Font Awesome 40 | import { faEye } from '@fortawesome/free-regular-svg-icons'; 41 | import { faVuejs, faPhp, faLaravel, faGithub } from '@fortawesome/free-brands-svg-icons'; 42 | import { faInfoCircle, faTable, faProjectDiagram, faRestroom, faHammer, faEllipsisV, faBars } from '@fortawesome/free-solid-svg-icons'; 43 | 44 | library.add( 45 | faBars, 46 | faEllipsisV, 47 | faVuejs, 48 | faPhp, 49 | faGithub, 50 | faLaravel, 51 | faTable, 52 | faProjectDiagram, 53 | faInfoCircle, 54 | faEye, 55 | faRestroom, 56 | faHammer 57 | ); 58 | 59 | Vue.component('font-awesome-icon', FontAwesomeIcon); 60 | 61 | import formatDateFilter from './filters/format-date'; 62 | import padDigitFilter from './filters/pad-digit'; 63 | 64 | //Filters 65 | Vue.filter('formatDate', formatDateFilter); 66 | Vue.filter('padDigit', padDigitFilter); 67 | 68 | //VueRouter 69 | const router = new VueRouter({ 70 | mode: "history", 71 | base: "/", 72 | routes, 73 | scrollBehavior () { 74 | return { x: 0, y: 0 } 75 | }, 76 | }); 77 | 78 | //Turn off production tips 79 | Vue.config.productionTip = false; 80 | 81 | //VueX 82 | import state from './state'; 83 | import mutations from './mutations'; 84 | 85 | const store = new Vuex.Store({ 86 | state, 87 | mutations, 88 | plugins: [createPersistedState()], 89 | }); 90 | 91 | //App Instance 92 | new Vue({ 93 | store, 94 | router, 95 | render: h => h(App), 96 | }).$mount('#app'); 97 | -------------------------------------------------------------------------------- /documentation/markdown/events/table.md: -------------------------------------------------------------------------------- 1 | ## Datatable Events 2 | | Name | Description 3 | | --- | --- | 4 | | loading | The table is loading new data | 5 | | finished-loading | The table has finished loading new data | 6 | | row-clicked | The table has had a row clicked | 7 | | on-table-props-changed | The table is about to reload due to a state change | 8 | 9 | -------------------------------------------------------------------------------- /documentation/markdown/examples/basic.md: -------------------------------------------------------------------------------- 1 | ## HTML 2 | 3 | ```html 4 | 10 | ``` 11 | 12 | ## Javascript 13 | ```javascript 14 | export default { 15 | data() { 16 | return { 17 | columns: [ 18 | { 19 | label: 'ID', 20 | name: 'id', 21 | orderable: true, 22 | }, 23 | { 24 | label: 'Name', 25 | name: 'name', 26 | orderable: true, 27 | }, 28 | { 29 | label: 'Cost (£)', 30 | name: 'cost', 31 | orderable: true, 32 | transform: ({data, name}) => `£${data[name]}`, 33 | }, 34 | { 35 | label: 'Email', 36 | name: 'email', 37 | orderable: true, 38 | }, 39 | ] 40 | } 41 | }, 42 | } 43 | ``` 44 | -------------------------------------------------------------------------------- /documentation/markdown/examples/crud.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamesdordoy/laravel-vue-datatable/74551479adf10cfd702d6a6d03c10ac13f1e49e1/documentation/markdown/examples/crud.md -------------------------------------------------------------------------------- /documentation/markdown/examples/custom-filters.md: -------------------------------------------------------------------------------- 1 | ### Datatable 2 | 3 | ```html 4 | 8 |
9 |
10 |
11 | 14 |
15 |
16 | 23 |
24 |
25 | 30 |
31 |
32 |
33 |
34 | ``` 35 | 36 | ```javascript 37 | data() { 38 | return { 39 | url: "http://example.test", 40 | columns: [ 41 | ... 42 | ], 43 | filters: { 44 | isActive: '', 45 | }, 46 | } 47 | }, 48 | ``` 49 | 50 | This added `isActive` filter will be send to the Laravel backend and can be used to manipulate the results: 51 | 52 | ```php 53 | $query = User::eloquentQuery( 54 | $request->input('column'), 55 | $request->input('dir'), 56 | $request->input('search') 57 | ); 58 | 59 | $isActive = $request->input('isActive'); 60 | 61 | if (isset($isActive)) { 62 | $query->where("is_active", $isActive); 63 | } 64 | 65 | $data = $query->paginate($request->input('length')); 66 | 67 | return new DataTableCollectionResource($data); 68 | ``` -------------------------------------------------------------------------------- /documentation/markdown/examples/injecting-dynamic-components.md: -------------------------------------------------------------------------------- 1 | ### Example Button Component 2 | 3 | > (ExampleButton.vue) 4 | 5 | ```html 6 | 15 | ``` 16 | 17 | ```javascript 18 | export default { 19 | props: { 20 | data: {}, 21 | name: {}, 22 | click: { 23 | type: Function, 24 | default: () => {} 25 | }, 26 | classes: { 27 | type: Object, 28 | default: () => ({ 29 | 'btn': true, 30 | 'btn-primary': true, 31 | 'btn-sm': true, 32 | }), 33 | }, 34 | } 35 | } 36 | ``` 37 | 38 | ### Example Image Component 39 | 40 | > (ExampleImageComponent.vue) 41 | 42 | ```html 43 | 46 | ``` 47 | 48 | ```javascript 49 | export default { 50 | props: { 51 | data: {} 52 | } 53 | } 54 | ``` 55 | 56 | 57 | ### Datatable Button Example 58 | 59 | > (UserDatatable.vue) 60 | 61 | ``` html 62 | 68 | ``` 69 | 70 | ```javascript 71 | import ExampleButton './ExampleButton.vue'; 72 | import ExampleImageComponent './ExampleImageComponent.vue'; 73 | 74 | export default { 75 | data() { 76 | return { 77 | columns: [ 78 | { 79 | label: 'ID', 80 | name: 'id', 81 | orderable: true, 82 | }, 83 | { 84 | label: 'Name', 85 | name: 'name', 86 | orderable: true, 87 | }, 88 | { 89 | label: 'Email', 90 | name: 'email', 91 | orderable: true, 92 | }, 93 | { 94 | label: 'Profile Image', 95 | name: 'img', 96 | orderable: true, 97 | component: ExampleImageComponent, 98 | }, 99 | { 100 | label: '', 101 | name: 'View', 102 | orderable: false, 103 | classes: { 104 | 'btn': true, 105 | 'btn-primary': true, 106 | 'btn-sm': true, 107 | }, 108 | event: "click", 109 | handler: this.displayRow, 110 | component: ExampleButton, 111 | }, 112 | ] 113 | } 114 | }, 115 | components: { 116 | // eslint-disable-next-line 117 | ExampleButton, 118 | }, 119 | methods: { 120 | displayRow(data) { 121 | alert(`You clicked row ${data.id}`); 122 | }, 123 | }, 124 | } 125 | ``` 126 | -------------------------------------------------------------------------------- /documentation/markdown/examples/joins-js.md: -------------------------------------------------------------------------------- 1 | > Datatable.vue 2 | ```html 3 | 9 | ``` 10 | 11 | ```javascript 12 | export default { 13 | data() { 14 | return { 15 | columns: [ 16 | { 17 | label: 'ID', 18 | name: 'id', 19 | orderable: true, 20 | }, 21 | { 22 | label: 'Department Name', 23 | name: 'department_name', 24 | columnName: 'departments.name', 25 | orderable: true, 26 | }, 27 | { 28 | label: 'Role Name', 29 | name: 'role_name', 30 | columnName: 'roles.name', 31 | orderable: true, 32 | }, 33 | { 34 | label: 'Name', 35 | name: 'user_name', 36 | columnName: 'users.name', 37 | orderable: true, 38 | }, 39 | 40 | { 41 | label: 'Email', 42 | name: 'email', 43 | orderable: true, 44 | }, 45 | ] 46 | } 47 | }, 48 | } 49 | ``` -------------------------------------------------------------------------------- /documentation/markdown/examples/joins-php.md: -------------------------------------------------------------------------------- 1 | ```php 2 | input('search'); 16 | $orderBy = $request->input('column'); 17 | $orderBydir = $request->input("dir"); 18 | $length = $request->input('length'); 19 | 20 | $data = DB::table('users') 21 | ->join('roles', 'roles.id', '=', 'users.role_id') 22 | ->join('departments', 'departments.id', '=', 'roles.department_id') 23 | ->select( 24 | 'roles.name as role_name', 25 | 'users.id', 26 | 'users.cost', 27 | 'users.name as user_name', 28 | 'users.email', 29 | 'departments.name as department_name' 30 | ) 31 | ->where("users.name", "LIKE", "%$searchValue%") 32 | ->orWhere('users.email', "LIKE", "%$searchValue%") 33 | ->orWhere('roles.name', "LIKE", "%$searchValue%") 34 | ->orWhere('departments.name', "LIKE", "%$searchValue%") 35 | ->orderBy($orderBy, $orderBydir) 36 | ->paginate($length); 37 | 38 | return new DataTableCollectionResource($data); 39 | } 40 | } 41 | ``` -------------------------------------------------------------------------------- /documentation/markdown/examples/loading-animations.md: -------------------------------------------------------------------------------- 1 | ```html 2 | 17 | ``` 18 | 19 | ```javascript 20 | // Import component 21 | import Loading from 'vue-loading-overlay'; 22 | // Import stylesheet 23 | import 'vue-loading-overlay/dist/vue-loading.css'; 24 | 25 | export default { 26 | data() { 27 | return { 28 | isLoading: false, 29 | columns: [ 30 | { 31 | label: 'ID', 32 | name: 'id', 33 | orderable: true, 34 | }, 35 | { 36 | label: 'Name', 37 | name: 'name', 38 | orderable: true, 39 | }, 40 | { 41 | label: 'Email', 42 | name: 'email', 43 | orderable: true, 44 | }, 45 | ] 46 | } 47 | }, 48 | } 49 | ``` 50 | -------------------------------------------------------------------------------- /documentation/markdown/examples/modal.md: -------------------------------------------------------------------------------- 1 | > Modal.vue 2 | ``` html 3 | 23 | ``` 24 | 25 | ``` javascript 26 | export default { 27 | props: { 28 | row: { 29 | type: Object, 30 | default: () => ({}), 31 | } 32 | } 33 | } 34 | ``` 35 |
36 | 37 | > ModalButton.vue 38 | ```html 39 | 49 | ``` 50 | 51 | ``` javascript 52 | 63 | ``` 64 |
65 | 66 | > Datatable.vue 67 | ```html 68 | 77 | ``` 78 | 79 | ```javascript 80 | 81 | import Modal from './MyModal'; 82 | import ModalButton from './MyModalButton'; 83 | 84 | export default { 85 | components: { 86 | Modal, 87 | ModalButton, 88 | }, 89 | data() { 90 | return { 91 | columns: [ 92 | { 93 | label: 'ID', 94 | name: 'id', 95 | orderable: true, 96 | }, 97 | { 98 | label: 'Name', 99 | name: 'name', 100 | orderable: true, 101 | }, 102 | { 103 | label: 'Email', 104 | name: 'email', 105 | orderable: true, 106 | }, 107 | { 108 | label: 'View', 109 | name: '', 110 | orderable: false, 111 | component: ModalButton, 112 | event: "click", 113 | handler: this.updateSelectedModal, 114 | }, 115 | ], 116 | selectedRow: {}, 117 | } 118 | }, 119 | methods: { 120 | updateSelectedModal(data) { 121 | this.selectedRow = data; 122 | } 123 | } 124 | } 125 | ``` 126 | -------------------------------------------------------------------------------- /documentation/markdown/examples/override-filters-and-pagination.md: -------------------------------------------------------------------------------- 1 | ### Pagination Changed On Datatable 2 | 3 | ```html 4 | 7 |
8 | 29 |
30 |
31 | ``` 32 | 33 | Once the URL has been updated by your customer paginator or filters, the table will re-render. Alternatively, if updating the URL is troublesome, different table filters can be manipulated by your filters using the v-model directive: 34 | 35 | ### Filter Changed On Datatable 36 | 37 | > (DatatableFilter.vue) 38 | 39 | This example filter will control the length of the table manipulating the tableData.length property using v-model and will make use of a custom [select component](https://github.com/sagalbot/vue-select) to make use of predictive results. 40 | 41 | ```html 42 | 45 |
46 |
47 |
48 | 51 |
52 |
53 | 59 | 60 |
61 |
62 |
63 |
64 | ``` 65 | 66 | ```javascript 67 | import vSelect from 'vue-select'; 68 | import 'vue-select/dist/vue-select.css'; 69 | 70 | export default { 71 | data() { 72 | return { 73 | url: "http://example.com/users", 74 | selectOptions: [], 75 | } 76 | }, 77 | components: { 78 | vSelect, 79 | }, 80 | created() { 81 | this.searchUsersByName(); 82 | }, 83 | methods: { 84 | updateSelectedUser(row, tableData) { 85 | if (row) { 86 | tableData.search = row.name; 87 | } else { 88 | tableData.search = ''; 89 | } 90 | }, 91 | searchUsersByName(term = '') { 92 | axios.get(this.url + term) 93 | .then(response => { 94 | this.selectOptions = response.data; 95 | }); 96 | } 97 | } 98 | } 99 | ``` -------------------------------------------------------------------------------- /documentation/markdown/examples/override-table-body.md: -------------------------------------------------------------------------------- 1 | ```html 2 | 26 | ``` 27 | 28 | ```js 29 | export default { 30 | data() { 31 | return { 32 | columns: [ 33 | { 34 | label: 'ID', 35 | name: 'id', 36 | orderable: true, 37 | }, 38 | { 39 | label: 'Name', 40 | name: 'name', 41 | orderable: true, 42 | }, 43 | { 44 | label: 'Email', 45 | name: 'email', 46 | orderable: true, 47 | }, 48 | ] 49 | } 50 | }, 51 | methods: { 52 | showRowNumber(id) { 53 | alert(`you clicked row ${id}`); 54 | } 55 | } 56 | } 57 | ``` -------------------------------------------------------------------------------- /documentation/markdown/examples/override-table-header.md: -------------------------------------------------------------------------------- 1 | ```html 2 | 18 | ``` 19 | 20 | ```js 21 | export default { 22 | data() { 23 | return { 24 | sortOrders: {}, 25 | columns: [ 26 | { 27 | label: 'ID', 28 | name: 'id', 29 | orderable: true, 30 | }, 31 | { 32 | label: 'Name', 33 | name: 'name', 34 | orderable: true, 35 | }, 36 | { 37 | label: 'Email', 38 | name: 'email', 39 | orderable: true, 40 | }, 41 | ] 42 | } 43 | }, 44 | mounted() { 45 | this.columns.forEach((column) => { 46 | this.sortOrders[column.name] = -1; 47 | }); 48 | }, 49 | methods: { 50 | sort(key, tableProps) { 51 | tableProps.column = key; 52 | this.sortOrders[key] = this.sortOrders[key] * -1; 53 | tableProps.dir = this.sortOrders[key] === 1 ? 'desc' : 'asc'; 54 | } 55 | } 56 | } 57 | ``` -------------------------------------------------------------------------------- /documentation/markdown/examples/own-data.md: -------------------------------------------------------------------------------- 1 | ```html 2 | 9 | ``` 10 | 11 | ```javascript 12 | export default { 13 | data() { 14 | return { 15 | url: "http://example.test", 16 | data: {}, 17 | tableProps: { 18 | search: '', 19 | length: 10, 20 | column: 'id', 21 | dir: 'asc' 22 | }, 23 | columns: [ 24 | { 25 | label: 'ID', 26 | name: 'id', 27 | orderable: true, 28 | }, 29 | { 30 | label: 'Name', 31 | name: 'name', 32 | orderable: true, 33 | }, 34 | { 35 | label: 'Email', 36 | name: 'email', 37 | orderable: true, 38 | }, 39 | ] 40 | } 41 | }, 42 | created() { 43 | this.getData(this.url); 44 | }, 45 | methods: { 46 | getData(url = this.url, options = this.tableProps) { 47 | axios.get(url, { 48 | params: options 49 | }) 50 | .then(response => { 51 | this.data = response.data; 52 | }) 53 | // eslint-disable-next-line 54 | .catch(errors => { 55 | //Handle Errors 56 | }) 57 | }, 58 | reloadTable(tableProps) { 59 | this.getData(this.url, tableProps); 60 | } 61 | } 62 | } 63 | ``` 64 | -------------------------------------------------------------------------------- /documentation/markdown/examples/relationships/belongs-to-js.md: -------------------------------------------------------------------------------- 1 | #### JS 2 | 3 | > Datatable.vue 4 | 5 | ```js 6 | export default { 7 | data() { 8 | return { 9 | columns: [ 10 | { 11 | label: 'ID', 12 | name: 'id', 13 | columnName: 'users.id', 14 | orderable: true, 15 | }, 16 | { 17 | label: 'Name', 18 | name: 'name', 19 | columnName: 'users.name', 20 | orderable: true, 21 | }, 22 | { 23 | label: 'Email', 24 | name: 'email', 25 | columnName: 'users.email', 26 | orderable: true, 27 | }, 28 | { 29 | label: 'Role', 30 | name: 'role.name', 31 | columnName: 'roles.name', 32 | orderable: true, 33 | }, 34 | ] 35 | } 36 | }, 37 | } 38 | ``` -------------------------------------------------------------------------------- /documentation/markdown/examples/relationships/belongs-to-many-js.md: -------------------------------------------------------------------------------- 1 | #### JS 2 | 3 | > ExampleSelectCell.vue 4 | 5 | ```html 6 | 13 | ``` 14 | 15 | ```js 16 | export default { 17 | props: { 18 | data: {}, 19 | name: {} 20 | }, 21 | } 22 | ``` 23 | 24 | > Datatable.vue 25 | 26 | ```js 27 | import ExampleSelectCell from './ExampleSelectCell'; 28 | 29 | export default { 30 | data() { 31 | return { 32 | columns: [ 33 | { 34 | label: 'ID', 35 | name: 'id', 36 | columnName: 'users.id', 37 | orderable: true, 38 | }, 39 | { 40 | label: 'Name', 41 | name: 'name', 42 | columnName: 'users.name', 43 | orderable: true, 44 | }, 45 | { 46 | label: 'Email', 47 | name: 'email', 48 | columnName: 'users.email', 49 | orderable: true, 50 | }, 51 | { 52 | label: 'Departments', 53 | name: 'departments', 54 | columnName: 'departments.name', 55 | component: ExampleSelectCell, 56 | orderable: true, 57 | }, 58 | ] 59 | } 60 | }, 61 | } 62 | 63 | ``` 64 | -------------------------------------------------------------------------------- /documentation/markdown/examples/relationships/belongs-to-many-php.md: -------------------------------------------------------------------------------- 1 | #### PHP 2 | 3 | > Add the relationship to your model 4 | 5 | ```php 6 | [ 10 | "departments" => [ 11 | "model" => \App\Department::class, 12 | "foreign_key" => "role_id", 13 | "pivot" => [ 14 | "table_name" => "department_user", 15 | "primary_key" => "id", 16 | "foreign_key" => "department_id", 17 | "local_key" => "user_id", 18 | ], 19 | "order_by" => "name", 20 | "columns" => [ 21 | "name" => [ 22 | "searchable" => true, 23 | "orderable" => true, 24 | ] 25 | ], 26 | ], 27 | ] 28 | ]; 29 | 30 | public function departments() 31 | { 32 | return $this->belongsToMany(\App\Department::class, 'department_user', 'user_id', 'department_id'); 33 | } 34 | ``` 35 | 36 | > Attach the relationship to the query 37 | 38 | ```php 39 | input('column'), 51 | $request->input('dir'), 52 | $request->input('search'), 53 | [ 54 | "departments" 55 | ] 56 | ); 57 | 58 | $data = $query->paginate($request->input('length')); 59 | 60 | return new DataTableCollectionResource($data); 61 | } 62 | } 63 | ``` -------------------------------------------------------------------------------- /documentation/markdown/examples/relationships/belongs-to-php.md: -------------------------------------------------------------------------------- 1 | #### PHP 2 | 3 | > Add the relationship to your model 4 | 5 | ```php 6 | [ 10 | 'role' => [ 11 | "model" => \App\Role::class, 12 | 'foreign_key' => 'role_id', 13 | 'columns' => [ 14 | 'name' => [ 15 | 'searchable' => true, 16 | 'orderable' => true, 17 | ], 18 | ], 19 | ], 20 | ], 21 | ]; 22 | 23 | public function role() 24 | { 25 | return $this->belongsTo(\App\Role::class); 26 | } 27 | ``` 28 | 29 | > Attach the relationship to the query 30 | 31 | ```php 32 | input('column'), 44 | $request->input('dir'), 45 | $request->input('search'), 46 | [ 47 | "role", 48 | ] 49 | ); 50 | 51 | $data = $query->paginate($request->input('length')); 52 | 53 | return new DataTableCollectionResource($data); 54 | } 55 | } 56 | ``` -------------------------------------------------------------------------------- /documentation/markdown/examples/relationships/has-many-js.md: -------------------------------------------------------------------------------- 1 | #### JS 2 | 3 | > ExampleSelectCell.vue 4 | 5 | ```html 6 | 13 | ``` 14 | 15 | ```js 16 | export default { 17 | props: { 18 | data: {}, 19 | name: {} 20 | }, 21 | } 22 | ``` 23 | 24 | > Datatable.vue 25 | 26 | ```js 27 | import ExampleSelectCell from './ExampleSelectCell'; 28 | 29 | export default { 30 | data() { 31 | return { 32 | columns: [ 33 | { 34 | label: 'ID', 35 | name: 'id', 36 | columnName: 'users.id', 37 | orderable: true, 38 | }, 39 | { 40 | label: 'Name', 41 | name: 'name', 42 | columnName: 'users.name', 43 | orderable: true, 44 | }, 45 | { 46 | label: 'Email', 47 | name: 'email', 48 | columnName: 'users.email', 49 | orderable: true, 50 | }, 51 | { 52 | label: 'Telephone Numbers', 53 | name: 'telephone_numbers', 54 | columnName: 'telephone_numbers.name', 55 | component: ExampleSelectCell, 56 | orderable: true, 57 | }, 58 | ] 59 | } 60 | }, 61 | } 62 | 63 | ``` 64 | -------------------------------------------------------------------------------- /documentation/markdown/examples/relationships/has-many-php.md: -------------------------------------------------------------------------------- 1 | #### PHP 2 | 3 | > Add the relationship to your model 4 | 5 | ```php 6 | [ 10 | 'telephoneNumbers' => [ 11 | "model" => \App\TelephoneNumber::class, 12 | 'foreign_key' => 'user_id', 13 | 'columns' => [ 14 | 'name' => [ 15 | 'searchable' => true, 16 | 'orderable' => true, 17 | ], 18 | ], 19 | ], 20 | ], 21 | ]; 22 | 23 | public function telephoneNumbers() 24 | { 25 | return $this->hasMany(\App\TelephoneNumber::class); 26 | } 27 | ``` 28 | 29 | > Attach the relationship to the query 30 | 31 | ```php 32 | input('column'), 44 | $request->input('dir'), 45 | $request->input('search'), 46 | [ 47 | "telephoneNumbers", 48 | ] 49 | ); 50 | 51 | $data = $query->paginate($request->input('length')); 52 | 53 | return new DataTableCollectionResource($data); 54 | } 55 | } 56 | ``` -------------------------------------------------------------------------------- /documentation/markdown/examples/relationships/introduction.md: -------------------------------------------------------------------------------- 1 | > Add relationships to model 2 | 3 | ```php 4 | [ 19 | 'searchable' => true, 20 | 'orderable' => true, 21 | ], 22 | 'name' => [ 23 | 'searchable' => true, 24 | 'orderable' => true, 25 | ], 26 | 'email' => [ 27 | 'searchable' => true, 28 | 'orderable' => true, 29 | ], 30 | ]; 31 | 32 | protected $dataTableRelationships = [ 33 | "belongsTo" => [ 34 | ... 35 | ], 36 | "hasMany" => [ 37 | ... 38 | ], 39 | "belongsToMany" => [ 40 | ... 41 | ] 42 | ]; 43 | } 44 | 45 | ``` 46 | 47 | > BelongsTo 48 | 49 | ```php 50 | [ 54 | "role" => [ 55 | "model" => \App\Role::class, 56 | "foreign_key" => "role_id", 57 | "columns" => [ 58 | "name" => [ 59 | "searchable" => true, 60 | "orderable" => true, 61 | ], 62 | ], 63 | ], 64 | ], 65 | ]; 66 | ``` 67 | 68 | > HasMany 69 | 70 | ```php 71 | [ 75 | "telephoneNumbers" => [ 76 | "model" => \App\TelephoneNumber::class, 77 | "foreign_key" => "user_id", 78 | "columns" => [ 79 | "name" => [ 80 | "searchable" => true, 81 | "orderable" => true, 82 | ], 83 | ], 84 | ], 85 | ], 86 | ]; 87 | ``` 88 | 89 | > BelongsToMany 90 | 91 | ```php 92 | [ 96 | "departments" => [ 97 | "model" => \App\Department::class, 98 | "foreign_key" => "role_id", 99 | "pivot" => [ 100 | "table_name" => "department_user", 101 | "primary_key" => "id", 102 | "foreign_key" => "department_id", 103 | "local_key" => "user_id", 104 | ], 105 | "order_by" => "name", 106 | "columns" => [ 107 | "name" => [ 108 | "searchable" => true, 109 | "orderable" => true, 110 | ] 111 | ], 112 | ], 113 | ] 114 | ]; 115 | ``` -------------------------------------------------------------------------------- /documentation/markdown/examples/relationships/mysql-config.md: -------------------------------------------------------------------------------- 1 | > config/database.php - MySQL example 2 | 3 | ```php 4 | 'strict' => false, 5 | 6 | or add 7 | 8 | 'modes' => [ 9 | 'STRICT_ALL_TABLES', 10 | 'ERROR_FOR_DIVISION_BY_ZERO', 11 | 'NO_ZERO_DATE', 12 | 'NO_ZERO_IN_DATE', 13 | 'NO_AUTO_CREATE_USER', 14 | ], 15 | ``` -------------------------------------------------------------------------------- /documentation/markdown/examples/reloading-the-table.md: -------------------------------------------------------------------------------- 1 | If updates have been made to your dataset and you need to reload the table, you can attach a [ref](https://vuejs.org/v2/api/#vm-refs) to the table. Once the Vue.JS reference is attached, you are able to access the underlining methods of the component including the table's getData method. 2 | 3 | Alternatively, if you have custom filters applied and you would prefered they are retained, any adjustment to the url the table uses as a prop will reload the table. -------------------------------------------------------------------------------- /documentation/markdown/examples/styling.md: -------------------------------------------------------------------------------- 1 | ### Tailwind Config 2 | 3 | > (mixins/tailwind.js) 4 | 5 | Custom Class 6 | 7 | ```css 8 | .stripped-table:nth-child(even) { 9 | @apply bg-black; 10 | } 11 | ``` 12 | 13 | ```javascript 14 | export default { 15 | data() { 16 | return { 17 | classes: { 18 | 'table-container': { 19 | 'justify-center': true, 20 | 'w-full': true, 21 | 'flex': true, 22 | 'rounded': true, 23 | 'mb-6': true, 24 | 'shadow-md': true, 25 | }, 26 | table: { 27 | 'text-left': true, 28 | 'w-full': true, 29 | 'border-collapse': true, 30 | }, 31 | 't-head': { 32 | 'text-grey-dark': true, 33 | 'bg-black': true, 34 | 'border-grey-light': true, 35 | 'py-4': true, 36 | 'px-6': true, 37 | }, 38 | "t-body": { 39 | 'bg-grey-darkest': true, 40 | 41 | }, 42 | "t-head-tr": { 43 | 44 | }, 45 | "t-body-tr": { 46 | 'stripped-table': true, 47 | 'bg-grey-darkest': true, 48 | }, 49 | "td": { 50 | 'py-4': true, 51 | 'px-6': true, 52 | 'border-b': true, 53 | 'border-grey-light': true, 54 | 'text-grey-light': true, 55 | }, 56 | "th": { 57 | 'py-4': true, 58 | 'px-6': true, 59 | 'font-bold': true, 60 | 'uppercase': true, 61 | 'text-sm': true, 62 | 'text-grey-dark': true, 63 | 'border-b': true, 64 | 'border-grey-light': true, 65 | }, 66 | } 67 | }; 68 | }, 69 | } 70 | ``` 71 | 72 | ### Tailwind Datatable 73 | 74 | ```html 75 | 80 | 81 | 84 | 85 | 86 | 87 | 88 | 93 | 94 | 95 | 96 | ``` 97 | 98 | ```javascript 99 | 100 | import TailwindDatatable from '../mixins/tailwind.js'; 101 | 102 | export default { 103 | data() { 104 | return { 105 | url: 'http://vue-datatable.test/ajax', 106 | perPage: ['10', '25', '50'], 107 | columns: [ 108 | { 109 | label: 'ID', 110 | name: 'id', 111 | orderable: true, 112 | }, 113 | { 114 | label: 'Name', 115 | name: 'name', 116 | orderable: true, 117 | }, 118 | { 119 | label: 'Email', 120 | name: 'email', 121 | orderable: true, 122 | } 123 | ] 124 | } 125 | }, 126 | mixins: [ 127 | TailwindDatatable 128 | ] 129 | } 130 | ``` 131 | -------------------------------------------------------------------------------- /documentation/markdown/install.md: -------------------------------------------------------------------------------- 1 | ## Component Installation 2 | 3 | 4 | ```bash 5 | npm install laravel-vue-datatable 6 | ``` 7 | 8 | or 9 | 10 | ```bash 11 | yarn add laravel-vue-datatable 12 | ``` 13 | 14 | ### Register the Plugin 15 | 16 | ```javascript 17 | import DataTable from 'laravel-vue-datatable'; 18 | 19 | Vue.use(DataTable); 20 | ``` -------------------------------------------------------------------------------- /documentation/markdown/introduction.md: -------------------------------------------------------------------------------- 1 | # Laravel Vue Datatable 2 | A Vue.js datatable component for Laravel that works with Bootstrap & Tailwind. 3 | 4 | ## Requirements 5 | 6 | * [Vue.js](https://vuejs.org/) 2.x 7 | * [Laravel](http://laravel.com/docs/) 5.x + 8 | * [Bootstrap](http://getbootstrap.com/) 4 (Optional) 9 | -------------------------------------------------------------------------------- /documentation/markdown/laravel/adding-trait.md: -------------------------------------------------------------------------------- 1 | ## Use the Trait 2 | 3 | This trait is optional and provides a methods for filtering your data based on the attributes set in the model. If you would like more control on how the data is filtered, feel free to omit this trait use your own filtering methods. Just remember to paginate the results for the API Resource! 4 | 5 | 6 | ```php 7 | [ 22 | 'searchable' => false, 23 | ], 24 | 'name' => [ 25 | 'searchable' => true, 26 | ], 27 | 'email' => [ 28 | 'searchable' => true, 29 | ] 30 | ]; 31 | 32 | protected $dataTableRelationships = [ 33 | // 34 | ]; 35 | } 36 | ``` -------------------------------------------------------------------------------- /documentation/markdown/laravel/controller-resource.md: -------------------------------------------------------------------------------- 1 | ## Use the Controller Resource 2 | 3 | The Collection Resource is expecting a paginated collection, so feel free to use your own queries and omit the provided trait query if your require more complex filtering. 4 | 5 | ```php 6 | input('length'); 19 | $sortBy = $request->input('column'); 20 | $orderBy = $request->input('dir'); 21 | $searchValue = $request->input('search'); 22 | 23 | $query = User::eloquentQuery($sortBy, $orderBy, $searchValue); 24 | 25 | $data = $query->paginate($length); 26 | 27 | return new DataTableCollectionResource($data); 28 | } 29 | } 30 | ``` -------------------------------------------------------------------------------- /documentation/markdown/laravel/install.md: -------------------------------------------------------------------------------- 1 | ## Package Installation 2 | 3 | ```bash 4 | composer require jamesdordoy/laravelvuedatatable 5 | ``` 6 |
7 | 8 | ## Add Service Provider 9 | Add the following line to your \config\app.php 10 | 11 | ```php 12 | JamesDordoy\LaravelVueDatatable\Providers\LaravelVueDatatableServiceProvider::class, 13 | ``` 14 |
15 | 16 | ## Publish the Config 17 | ```php 18 | php artisan vendor:publish --provider="JamesDordoy\LaravelVueDatatable\Providers\LaravelVueDatatableServiceProvider" 19 | ``` -------------------------------------------------------------------------------- /documentation/markdown/laravel/options.md: -------------------------------------------------------------------------------- 1 | ## Package Options 2 | 3 | ```php 4 | [ 5 | 'models' => [ 6 | "search_term" => "searchable", 7 | "order_term" => "orderable", 8 | ], 9 | "default_order_by" => "id", 10 | ]; 11 | ``` -------------------------------------------------------------------------------- /documentation/markdown/props/cell.md: -------------------------------------------------------------------------------- 1 | ## Cell Props 2 | | Name | Type | Default | Description 3 | | --- | --- | --- | --- | 4 | | name | String | "" | The column name | 5 | | value | Object | {} | The table row | 6 | | meta | Object | {} | (optional) Additional values that are parsed to cell | 7 | | comp | Component | Vue.Component | (optional) A dynamic component that can be injected | 8 | | event | String | "" | (optional) Event type to parse to the component e.g. click, focus etc. | 9 | | handler | Function | () => {} | (optional) Function to parse for the event handler | 10 | | classes | Object | {} | (optional) Cell classes to parse | -------------------------------------------------------------------------------- /documentation/markdown/props/column.md: -------------------------------------------------------------------------------- 1 | ## Column Props 2 | | Name | Type | Default | Description 3 | | --- | --- | --- | --- | 4 | | label | String | "" | The table column label to be displayed as the column heading | 5 | | name | String | "" | The table column header name. You can also access nested properties e.g. a query using a with relationship using the dot notation. | 6 | | columnName | String | "" | (optional) The backend column name if the provided data keys do not match with the backend database. It may also be required to prefix the column name with the table name e.g. users.name to avoid issues with Integrity constraint violation when joining tables | 7 | | width | Number | 0 | (optional) The table column width | 8 | | orderable | Boolean | false | (optional) Is the column orderable in the datatable | 9 | | component | Component | null | (optional) A dynamic component that can be injected | 10 | | event | String | "" | (optional) Event type to parse to the component e.g. click, focus etc. | 11 | | handler | Function | () => {} | (optional) Function to parse for the event handler | 12 | | classes | Object | {} | (optional) Component classes to parse | 13 | | meta | Object | {} | (optional) Additional values that are parsed to component | -------------------------------------------------------------------------------- /documentation/markdown/props/table.md: -------------------------------------------------------------------------------- 1 | ## Datatable Props 2 | | Name | Type | Default | Description 3 | | --- | --- | --- | --- | 4 | | url | String | "/" | The JSON url | 5 | | columns | Array | [] | The table columns | 6 | | order-by | String | "id" | (optional) The default column to order your data by | 7 | | order-dir | String | "asc" | (optional) The default order by direction | 8 | | per-page | Array | ['10','25','50'] | (optional) Amount to be displayed | 9 | | theme | String | "light" | (optional) Must be dark or light | 10 | | translate | Object | { nextButton: 'Next', previousButton: 'Previous', placeholderSearch: 'Search Table'} | (optional) used to overwrite the default pagination button text and search input placeholder | 11 | | debounce-delay | Number | 0 | (optional) Adds a debounce delay to the get request when searching | 12 | | add-filters-to-url | Boolean | false |

(optional) Will adjust the current url to keep track of used filters and will also store them in local storage.

| 13 | | classes | Object | See Below | (optional) Table classes | 14 | | pagination | Object | {} | (optional) props for [gilbitron/laravel-vue-pagination](https://github.com/gilbitron/laravel-vue-pagination#props) | 15 | | headers | Object | {} | Additional headers to pass route e.g. bearer token | 16 |
17 | 18 | ### Default Classes 19 | ```json 20 | { 21 | "table-container": { 22 | "table-responsive": true, 23 | }, 24 | "table": { 25 | "table": true, 26 | "table-striped": true, 27 | "border": true, 28 | }, 29 | "t-head": { 30 | 31 | }, 32 | "t-body": { 33 | 34 | }, 35 | "t-head-tr": { 36 | 37 | }, 38 | "t-body-tr": { 39 | 40 | }, 41 | "td": { 42 | 43 | }, 44 | "th": { 45 | 46 | }, 47 | } 48 | ``` 49 | -------------------------------------------------------------------------------- /documentation/mixins/CodeExample.js: -------------------------------------------------------------------------------- 1 | import { mapState } from 'vuex'; 2 | 3 | export default { 4 | data() { 5 | return { 6 | code: this.$store.state.showCode, 7 | } 8 | }, 9 | computed: mapState(['showCode']), 10 | watch: { 11 | showCode(newValue) { 12 | this.code = newValue; 13 | } 14 | }, 15 | mounted() { 16 | let styleingFunction = element => { 17 | element.style.overflowWrap = "normal"; 18 | element.style.whiteSpace = "normal"; 19 | }; 20 | 21 | let paragraphs = document.querySelectorAll("p"); 22 | let h1 = document.querySelectorAll("h1"); 23 | let h2 = document.querySelectorAll("h2"); 24 | let h3 = document.querySelectorAll("h3"); 25 | let h4 = document.querySelectorAll("h4"); 26 | let h5 = document.querySelectorAll("h5"); 27 | 28 | paragraphs.forEach(styleingFunction); 29 | h1.forEach(styleingFunction); 30 | h2.forEach(styleingFunction); 31 | h3.forEach(styleingFunction); 32 | h4.forEach(styleingFunction); 33 | h5.forEach(styleingFunction); 34 | } 35 | } -------------------------------------------------------------------------------- /documentation/mixins/CorrectTableStyling.js: -------------------------------------------------------------------------------- 1 | export default { 2 | mounted() { 3 | document.querySelectorAll("table").forEach(table => { 4 | table.classList.add("table"); 5 | table.classList.add("table-bordered"); 6 | table.classList.add("table-striped"); 7 | }); 8 | } 9 | } -------------------------------------------------------------------------------- /documentation/mixins/CorrectTextStyling.js: -------------------------------------------------------------------------------- 1 | export default { 2 | mounted() { 3 | let styleingFunction = element => { 4 | element.style.overflowWrap = "normal"; 5 | element.style.whiteSpace = "normal"; 6 | }; 7 | 8 | let paragraphs = document.querySelectorAll("p"); 9 | let h1 = document.querySelectorAll("h1"); 10 | let td = document.querySelectorAll("td"); 11 | let h2 = document.querySelectorAll("h2"); 12 | let h3 = document.querySelectorAll("h3"); 13 | let h4 = document.querySelectorAll("h4"); 14 | let h5 = document.querySelectorAll("h5"); 15 | 16 | paragraphs.forEach(styleingFunction); 17 | h1.forEach(styleingFunction); 18 | h2.forEach(styleingFunction); 19 | h3.forEach(styleingFunction); 20 | h4.forEach(styleingFunction); 21 | h5.forEach(styleingFunction); 22 | 23 | td.forEach(styleingFunction); 24 | } 25 | } -------------------------------------------------------------------------------- /documentation/mutations.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | showCode (state) { 3 | state.showCode = true; 4 | }, 5 | hideCode (state) { 6 | state.showCode = false; 7 | } 8 | }; -------------------------------------------------------------------------------- /documentation/routes.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | path: '/laravel-vue-datatable', 4 | component: require('../documentation/views/Introduction.vue').default, 5 | }, 6 | { 7 | path: '/laravel-vue-datatable/installation', 8 | component: require('../documentation/views/Install.vue').default, 9 | }, 10 | { 11 | path: '/laravel-vue-datatable/props/table', 12 | component: require('../documentation/views/props/Table.vue').default, 13 | }, 14 | { 15 | path: '/laravel-vue-datatable/props/column', 16 | component: require('../documentation/views/props/Column.vue').default, 17 | }, 18 | { 19 | path: '/laravel-vue-datatable/props/cell', 20 | component: require('../documentation/views/props/Cell.vue').default, 21 | }, 22 | { 23 | path: '/laravel-vue-datatable/events/table', 24 | component: require('../documentation/views/events/Table.vue').default, 25 | }, 26 | { 27 | path: '/laravel-vue-datatable/laravel/installation', 28 | component: require('../documentation/views/laravel/Install.vue').default, 29 | }, 30 | { 31 | path: '/laravel-vue-datatable/laravel/options', 32 | component: require('../documentation/views/laravel/Options.vue').default, 33 | }, 34 | { 35 | path: '/laravel-vue-datatable/laravel/trait', 36 | component: require('../documentation/views/laravel/AddingTrait.vue').default, 37 | }, 38 | { 39 | path: '/laravel-vue-datatable/laravel/controller-resource', 40 | component: require('../documentation/views/laravel/ControllerResource.vue').default, 41 | }, 42 | { 43 | path: '/laravel-vue-datatable/examples/basic', 44 | component: require('../documentation/views/examples/Basic.vue').default, 45 | }, 46 | { 47 | path: '/laravel-vue-datatable/examples/tailwind', 48 | component: require('../documentation/views/examples/Tailwind.vue').default, 49 | }, 50 | { 51 | path: '/laravel-vue-datatable/examples/loading-animations', 52 | component: require('../documentation/views/examples/LoadingAnimations.vue').default, 53 | }, 54 | { 55 | path: '/laravel-vue-datatable/examples/relationships', 56 | component: require('../documentation/views/examples/relationships/Introduction.vue').default, 57 | }, 58 | { 59 | path: '/laravel-vue-datatable/examples/belongs-to', 60 | component: require('../documentation/views/examples/relationships/BelongsTo.vue').default, 61 | }, 62 | { 63 | path: '/laravel-vue-datatable/examples/has-many', 64 | component: require('../documentation/views/examples/relationships/HasMany.vue').default, 65 | }, 66 | { 67 | path: '/laravel-vue-datatable/examples/belongs-to-many', 68 | component: require('../documentation/views/examples/relationships/BelongsToMany.vue').default, 69 | }, 70 | { 71 | path: '/laravel-vue-datatable/examples/injecting-dynamic-components', 72 | component: require('../documentation/views/examples/InjectingDynamicComponents.vue').default, 73 | }, 74 | { 75 | path: '/laravel-vue-datatable/examples/override-filters', 76 | component: require('../documentation/views/examples/OverrideFiltersAndPagination.vue').default, 77 | }, 78 | { 79 | path: '/laravel-vue-datatable/examples/override-table-body', 80 | component: require('../documentation/views/examples/OverrideTableBody.vue').default, 81 | }, 82 | { 83 | path: '/laravel-vue-datatable/examples/override-table-header', 84 | component: require('../documentation/views/examples/OverrideTableHeader.vue').default, 85 | }, 86 | { 87 | path: '/laravel-vue-datatable/examples/custom-filters', 88 | component: require('../documentation/views/examples/CustomFilters.vue').default, 89 | }, 90 | { 91 | path: '/laravel-vue-datatable/examples/reload-table', 92 | component: require('../documentation/views/examples/ReloadingTheTable.vue').default, 93 | }, 94 | 95 | { 96 | path: '/laravel-vue-datatable/examples/styling', 97 | component: require('../documentation/views/examples/Styling.vue').default, 98 | }, 99 | { 100 | path: '/laravel-vue-datatable/examples/modal', 101 | component: require('../documentation/views/examples/Modal.vue').default, 102 | }, 103 | { 104 | path: '/laravel-vue-datatable/examples/crud', 105 | component: require('../documentation/views/Soon.vue').default, 106 | }, 107 | { 108 | path: '/laravel-vue-datatable/examples/own-data', 109 | component: require('../documentation/views/examples/UsingYourOwnData.vue').default, 110 | }, 111 | { 112 | path: '/laravel-vue-datatable/examples/joins', 113 | component: require('../documentation/views/examples/Joins.vue').default, 114 | }, 115 | ]; -------------------------------------------------------------------------------- /documentation/state.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | showCode: false 3 | }; -------------------------------------------------------------------------------- /documentation/views/Install.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 30 | -------------------------------------------------------------------------------- /documentation/views/Introduction.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 24 | -------------------------------------------------------------------------------- /documentation/views/Soon.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /documentation/views/events/Table.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 26 | -------------------------------------------------------------------------------- /documentation/views/examples/Basic.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 82 | -------------------------------------------------------------------------------- /documentation/views/examples/CRUD.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 65 | -------------------------------------------------------------------------------- /documentation/views/examples/CustomFilters.vue: -------------------------------------------------------------------------------- 1 | 56 | 57 | 132 | -------------------------------------------------------------------------------- /documentation/views/examples/InjectingDynamicComponents.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 109 | -------------------------------------------------------------------------------- /documentation/views/examples/Joins.vue: -------------------------------------------------------------------------------- 1 | 82 | 83 | 154 | -------------------------------------------------------------------------------- /documentation/views/examples/LoadingAnimations.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 84 | -------------------------------------------------------------------------------- /documentation/views/examples/Modal.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 97 | -------------------------------------------------------------------------------- /documentation/views/examples/OverrideFiltersAndPagination.vue: -------------------------------------------------------------------------------- 1 | 2 | 74 | 75 | 80 | 81 | 146 | -------------------------------------------------------------------------------- /documentation/views/examples/OverrideTableBody.vue: -------------------------------------------------------------------------------- 1 | 49 | 50 | 100 | -------------------------------------------------------------------------------- /documentation/views/examples/OverrideTableHeader.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 102 | -------------------------------------------------------------------------------- /documentation/views/examples/ReloadingTheTable.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 88 | -------------------------------------------------------------------------------- /documentation/views/examples/Styling.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 65 | -------------------------------------------------------------------------------- /documentation/views/examples/Tailwind.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | 39 | 114 | -------------------------------------------------------------------------------- /documentation/views/examples/UsingYourOwnData.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 99 | -------------------------------------------------------------------------------- /documentation/views/examples/relationships/BelongsTo.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 88 | -------------------------------------------------------------------------------- /documentation/views/examples/relationships/BelongsToMany.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 91 | -------------------------------------------------------------------------------- /documentation/views/examples/relationships/HasMany.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 90 | -------------------------------------------------------------------------------- /documentation/views/examples/relationships/Introduction.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 45 | -------------------------------------------------------------------------------- /documentation/views/laravel/AddingTrait.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 25 | -------------------------------------------------------------------------------- /documentation/views/laravel/ControllerResource.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 23 | -------------------------------------------------------------------------------- /documentation/views/laravel/Install.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 23 | -------------------------------------------------------------------------------- /documentation/views/laravel/Options.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 23 | -------------------------------------------------------------------------------- /documentation/views/props/Cell.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 26 | -------------------------------------------------------------------------------- /documentation/views/props/Column.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 26 | -------------------------------------------------------------------------------- /documentation/views/props/Table.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 26 | -------------------------------------------------------------------------------- /editor.conf: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | # Unix-style newlines with a newline ending every file 5 | [*] 6 | end_of_line = lf 7 | insert_final_newline = true 8 | 9 | # Matches multiple files with brace expansion notation 10 | # Set default charset 11 | [*.{js,py}] 12 | charset = utf-8 13 | 14 | # 4 space indentation 15 | [*.js,*.vue ] 16 | indent_style = space 17 | indent_size = 4 18 | 19 | # Tab indentation (no size specified) 20 | [Makefile] 21 | indent_style = tab 22 | 23 | # Indentation override for all JS under lib directory 24 | [lib/**.js] 25 | indent_style = space 26 | indent_size = 4 27 | 28 | # Matches the exact files either package.json or .travis.yml 29 | [{package.json,.travis.yml}] 30 | indent_style = space 31 | indent_size = 2 -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: '@vue/cli-plugin-unit-jest/presets/typescript-and-babel', 3 | "setupFilesAfterEnv": [ 4 | "/__tests__/bootstrap.js" 5 | ], 6 | "testPathIgnorePatterns": [ 7 | "/__tests__/bootstrap.*" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "laravel-vue-datatable", 3 | "version": "0.6.0", 4 | "description": "Vue.js datatable made with Laravel and Bootstrap in mind", 5 | "author": "James Dordoy ", 6 | "homepage": "https://jamesdordoy.github.io/laravel-vue-datatable/", 7 | "private": false, 8 | "main": "./dist/laravel-vue-datatable.common.js", 9 | "keywords": [ 10 | "vue", 11 | "component", 12 | "bootstrap", 13 | "tailwind.css", 14 | "laravel" 15 | ], 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/jamesdordoy/Laravel-Vue-Datatable.git" 19 | }, 20 | "scripts": { 21 | "serve": "vue-cli-service serve documentation/main.ts", 22 | "build": "vue-cli-service build --target lib src/plugin.ts", 23 | "build-docs": "vue-cli-service build documentation/main.ts", 24 | "lint": "vue-cli-service lint", 25 | "test": "jest" 26 | }, 27 | "dependencies": { 28 | "axios": "^0.27.2", 29 | "lodash.debounce": "^4.0.8", 30 | "lodash.defaultsdeep": "^4.6.1", 31 | "tailable-pagination": "^2.0.0", 32 | "vue": "^2.6.14" 33 | }, 34 | "devDependencies": { 35 | "@fortawesome/fontawesome-svg-core": "^1.2.22", 36 | "@fortawesome/free-brands-svg-icons": "^5.10.2", 37 | "@fortawesome/free-regular-svg-icons": "^5.10.2", 38 | "@fortawesome/free-solid-svg-icons": "^5.10.2", 39 | "@fortawesome/vue-fontawesome": "^0.1.7", 40 | "@fullhuman/postcss-purgecss": "^4.1.3", 41 | "@types/jest": "27.5.1", 42 | "@vue/vue2-jest": "27.0.0", 43 | "@vue/cli-plugin-babel": "^5.0.4", 44 | "@vue/cli-plugin-typescript": "^5.0.4", 45 | "@vue/cli-plugin-unit-jest": "^5.0.4", 46 | "@vue/cli-service": "^5.0.4", 47 | "@vue/test-utils": "1.3.0", 48 | "babel-jest": "27.0.0", 49 | "bootstrap": "^5.1.3", 50 | "core-js": "^3.22.7", 51 | "jest": "27.0.0", 52 | "jest-localstorage-mock": "^2.4.21", 53 | "jest-serializer-vue": "^2.0.2", 54 | "jquery": "^3.6.0", 55 | "moment": "^2.29.3", 56 | "node-sass": "^7.0.1", 57 | "popper.js": "^1.16.1", 58 | "prismjs": "^1.28.0", 59 | "rollup-plugin-vue": "^6.0.0", 60 | "sass-loader": "^13.0.0", 61 | "tailwindcss": "^2.2.19", 62 | "ts-jest": "^27.0.0", 63 | "typescript": "~4.7.2", 64 | "vue-class-component": "^7.2.6", 65 | "vue-jest": "^3.0.7", 66 | "vue-loading-overlay": "^3.4.2", 67 | "vue-markdown-loader": "^2.5.0", 68 | "vue-property-decorator": "^9.1.2", 69 | "vue-router": "^3.1.3", 70 | "vue-select": "^3.18.3", 71 | "vue-sweetalert2": "^5.0.5", 72 | "vue-template-compiler": "^2.6.14", 73 | "vuex": "^3.1.1", 74 | "vuex-persistedstate": "^4.1.0" 75 | }, 76 | "files": [ 77 | "dist/*", 78 | "src/*" 79 | ], 80 | "browserslist": [ 81 | "> 1%", 82 | "last 2 versions", 83 | "not ie <= 8" 84 | ] 85 | } 86 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | // postcss.config.js 2 | const autoprefixer = require('autoprefixer'); 3 | const tailwindcss = require('tailwindcss'); 4 | 5 | const postcssPurgecss = require(`@fullhuman/postcss-purgecss`); 6 | 7 | const purgecss = postcssPurgecss({ 8 | // Specify the paths to all of the template files in your project. 9 | content: [ 10 | './public/**/*.html', 11 | './documentation/**/*.vue', 12 | ], 13 | // Include any special characters you're using in this regular expression. 14 | // See: https://tailwindcss.com/docs/controlling-file-size/#understanding-the-regex 15 | defaultExtractor: content => content.match(/[\w-/:]+(? 2 | 3 | 4 | 5 | Single Page Apps for GitHub Pages 6 | 7 | 8 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Laravel Vue Datatable 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 44 | 45 | 46 | 49 |
50 | 51 | -------------------------------------------------------------------------------- /src/components/DataTableCell.vue: -------------------------------------------------------------------------------- 1 | 100 | -------------------------------------------------------------------------------- /src/components/DataTableFilters.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 86 | -------------------------------------------------------------------------------- /src/components/DataTableTh.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | -------------------------------------------------------------------------------- /src/components/Table.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 91 | -------------------------------------------------------------------------------- /src/functions/MergeClasses.ts: -------------------------------------------------------------------------------- 1 | export default function mergeClasses(...classlists) { 2 | const classes = []; 3 | 4 | for (let classlist in classlists) { 5 | if (classlists.hasOwnProperty(classlist)) { 6 | let list = classlists[`${classlist}`]; 7 | 8 | if (typeof list === "string") { 9 | classes.push(list); 10 | } else if (Array.isArray(list)) { 11 | classes.push(...list); 12 | } else if (typeof list === "object") { 13 | for (let cls in list) { 14 | if (list.hasOwnProperty(cls) && 15 | typeof list[`${cls}`] !== "object" && 16 | list[`${cls}`] && 17 | cls !== "!override") { 18 | classes.push(`${cls}`); 19 | } 20 | } 21 | } 22 | } 23 | } 24 | 25 | return [...new Set(classes)].join(" "); 26 | }; 27 | -------------------------------------------------------------------------------- /src/mixins/UrlFilters.js: -------------------------------------------------------------------------------- 1 | export default { 2 | methods: { 3 | getURLParameter(name) { 4 | return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search) || [null, ''])[1].replace(/\+/g, '%20')) || null; 5 | }, 6 | IsValidJSONString(str) { 7 | try { 8 | JSON.parse(str); 9 | } catch (e) { 10 | return false; 11 | } 12 | return true; 13 | }, 14 | checkParameters(tableData) { 15 | if (this.addFiltersToUrl) { 16 | let localStorage = window.localStorage; 17 | 18 | Object.keys(tableData).forEach(filter => { 19 | if (this.getURLParameter(filter)) { 20 | let value = this.getURLParameter(filter); 21 | 22 | if (! isNaN(value)) { 23 | this.tableData[filter] = Number(value); 24 | } 25 | else if (this.IsValidJSONString(value)) { 26 | this.tableData.filters = JSON.parse(value); 27 | } 28 | else { 29 | this.tableData[filter] = value; 30 | } 31 | 32 | } else if (localStorage.getItem(this.$options.name + "_" + filter)){ 33 | let value = localStorage.getItem(this.$options.name + "_" + filter); 34 | 35 | if (! isNaN(value)) { 36 | this.tableData[filter] = Number(value); 37 | } 38 | else if (this.IsValidJSONString(value)) { 39 | this.tableData.filters = JSON.parse(value); 40 | } 41 | else { 42 | this.tableData[filter] = value; 43 | } 44 | } 45 | }); 46 | 47 | this.updateParameters(this.tableData); 48 | } 49 | }, 50 | updateParameters(tableData) { 51 | 52 | let newFilters = {}; 53 | 54 | Object.keys(tableData).forEach(filter => { 55 | if (this.tableData[filter]) { 56 | newFilters[filter] = JSON.stringify(this.tableData[filter]); 57 | } else { 58 | newFilters[filter] = this.tableData[filter]; 59 | } 60 | }); 61 | 62 | for (let filter in newFilters) { 63 | localStorage.setItem(this.$options.name + "_" + filter, newFilters[filter]); 64 | } 65 | 66 | let parameters = Object.keys(newFilters).map(function(k) { 67 | return encodeURIComponent(k) + '=' + encodeURIComponent(newFilters[k]) 68 | }).join('&') 69 | 70 | let url = document.URL.split('?')[0]; 71 | 72 | window.history.pushState(newFilters, 'Title', url + "?" + parameters); 73 | }, 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/plugin.ts: -------------------------------------------------------------------------------- 1 | import DataTable from "./components/DataTable.vue"; 2 | import { TailablePagination } from 'tailable-pagination'; 3 | import DataTableCell from "./components/DataTableCell.vue"; 4 | 5 | export default { 6 | install(Vue) { 7 | Vue.component("data-table", DataTable); 8 | Vue.component("data-table-cell", DataTableCell); 9 | Vue.component("tailable-pagination", TailablePagination); 10 | }, 11 | }; 12 | 13 | export { DataTable, DataTableCell }; 14 | -------------------------------------------------------------------------------- /src/shims-tsx.d.ts: -------------------------------------------------------------------------------- 1 | 2 | import Vue, { VNode } from 'vue'; 3 | 4 | declare global { 5 | namespace JSX { 6 | // tslint:disable no-empty-interface 7 | interface Element extends VNode {} 8 | // tslint:disable no-empty-interface 9 | interface ElementClass extends Vue {} 10 | interface IntrinsicElements { 11 | [elem: string]: any; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import Vue from 'vue'; 3 | export default Vue; 4 | } 5 | -------------------------------------------------------------------------------- /src/themes/Bootstrap.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | "table-container": { 3 | "table-responsive": true 4 | }, 5 | "table": { 6 | "table": true, 7 | "table-striped": true, 8 | "border": true 9 | }, 10 | "t-head": {}, 11 | "t-body": {}, 12 | "td": {}, 13 | "th": {} 14 | } -------------------------------------------------------------------------------- /src/themes/Tailwind.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | "container": { 3 | "w-full": true 4 | }, 5 | "table-container": { 6 | "w-full overflow-x-auto rounded-t": true 7 | }, 8 | "table": { 9 | "min-w-full": true, 10 | }, 11 | "t-head": { 12 | "bg-gray-100": true, 13 | "border": true, 14 | }, 15 | "t-body": { 16 | "bg-white border-r border-l border-b": true, 17 | }, 18 | "t-body-tr": { 19 | "bg-white even:bg-gray-100": true, 20 | }, 21 | "td": { 22 | "px-4 py-3 whitespace-no-wrap border-b border-gray-200": true, 23 | }, 24 | "th": { 25 | "p-4 text-xs whitespace-no-wrap hover:cursor-pointer text-left": true, 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /src/validators/data-table-framework.ts: -------------------------------------------------------------------------------- 1 | export default (value) => { 2 | return [ 3 | 'bootstrap', 4 | 'tailwind', 5 | ].indexOf(value) !== -1; 6 | }; 7 | -------------------------------------------------------------------------------- /src/validators/data-table-order-dir.ts: -------------------------------------------------------------------------------- 1 | export default (value) => { 2 | return [ 3 | 'asc', 4 | 'desc', 5 | ].indexOf(value) !== -1; 6 | }; 7 | -------------------------------------------------------------------------------- /src/validators/data-table-theme.ts: -------------------------------------------------------------------------------- 1 | export default (value) => { 2 | return [ 3 | 'light', 4 | 'dark', 5 | ].indexOf(value) !== -1; 6 | }; 7 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | theme: { 3 | minWidth: { 4 | '0': '0', 5 | '1/6': '16.666667%', 6 | '1/2': '50%', 7 | '3/4': '75%', 8 | 'full': '100%', 9 | }, 10 | extend: {}, 11 | }, 12 | variants: { 13 | opacity: ['disabled'], 14 | cursor: ['disabled', 'hover'], 15 | zIndex: ['focus'], 16 | backgroundColor: ['hover', 'focus', 'even'], 17 | }, 18 | plugins: [], 19 | } 20 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": false, 6 | "jsx": "preserve", 7 | "importHelpers": true, 8 | "moduleResolution": "node", 9 | "experimentalDecorators": true, 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "sourceMap": true, 13 | "baseUrl": ".", 14 | "noImplicitThis": true, 15 | "types": [ 16 | "webpack-env", 17 | "jest" 18 | ], 19 | "paths": { 20 | "@/*": [ 21 | "src/*" 22 | ] 23 | }, 24 | "lib": [ 25 | "esnext", 26 | "dom", 27 | "dom.iterable", 28 | "scripthost" 29 | ] 30 | }, 31 | "include": [ 32 | "src/**/*.ts", 33 | "src/**/*.tsx", 34 | "src/**/*.vue", 35 | "tests/**/*.ts", 36 | "tests/**/*.tsx" 37 | ], 38 | "exclude": [ 39 | "node_modules" 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | module.exports = { 4 | publicPath: `/laravel-vue-datatable`, 5 | outputDir: path.resolve(__dirname, "./docs"), 6 | chainWebpack: config => { 7 | config.module.rule('md') 8 | .test(/\.md/) 9 | .use('vue-loader') 10 | .loader('vue-loader') 11 | .end() 12 | .use('vue-markdown-loader') 13 | .loader('vue-markdown-loader/lib/markdown-compiler') 14 | .options({ 15 | raw: true 16 | }) 17 | } 18 | } 19 | --------------------------------------------------------------------------------