Skip to content

Commit 8cbf427

Browse files
committed
Merge branch 'develop'
# Conflicts: # composer.json
2 parents e9dce78 + 8654ccf commit 8cbf427

10 files changed

+169
-56
lines changed

README.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -269,17 +269,19 @@ The `Table` has some additional properties to tweak its front-end behaviour.
269269
:striped="true"
270270
:prevent-overlapping-requests="false"
271271
:input-debounce-ms="1000"
272-
:preserve-scroll="true"
272+
:prevent-scroll="true"
273+
:active-classes="{text: 'text-red-500', border: 'border-red-300'}"
273274
/>
274275
</template>
275276
```
276277

277-
| Property | Description | Default |
278-
| --- | --- | --- |
279-
| striped | Adds a *striped* layout to the table. | `false` |
280-
| preventOverlappingRequests | Cancels a previous visit on new user input to prevent an inconsistent state. | `true` |
281-
| inputDebounceMs | Number of ms to wait before refreshing the table on user input. | 350 |
282-
| preventScroll | Configures the [Scroll preservation](https://inertiajs.com/scroll-management#scroll-preservation) behavior. You may also pass `table-top` to this property to scroll to the top of the table on new data. | false |
278+
| Property | Description | Default |
279+
|----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------|
280+
| striped | Adds a *striped* layout to the table. | `false` |
281+
| preventOverlappingRequests | Cancels a previous visit on new user input to prevent an inconsistent state. | `true` |
282+
| inputDebounceMs | Number of ms to wait before refreshing the table on user input. | 350 |
283+
| preventScroll | Configures the [Scroll preservation](https://inertiajs.com/scroll-management#scroll-preservation) behavior. You may also pass `table-top` to this property to scroll to the top of the table on new data. | false |
284+
| activeClasses | Configures the CSS classes to apply on active elements like filters & column buttons and sorting indicator | {text: 'text-green-400', border: 'border-green-300' } |
283285

284286
#### Custom column cells
285287

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
}
1212
],
1313
"require": {
14-
"php": "^7.4|^8.0|^8.1|^8.2",
15-
"illuminate/support": "^v9.0|^v10.0"
14+
"php": "^8.0|^8.1|^8.2",
15+
"illuminate/support": "^8.67|^v9.0|v10.0"
1616
},
1717
"require-dev": {
1818
"friendsofphp/php-cs-fixer": "^3.8",

js/Components/ButtonWithDropdown.vue

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77
:dusk="dusk"
88
:disabled="disabled"
99
class="w-full bg-white border rounded-md shadow-sm px-4 py-2 inline-flex justify-center text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
10-
:class="{'border-green-300': active, 'border-gray-300': !active, 'cursor-not-allowed': disabled }"
10+
:class="{
11+
[activeClasses.border]: props.active,
12+
'border-gray-300': !props.active,
13+
'cursor-not-allowed': props.disabled
14+
}"
1115
aria-haspopup="true"
1216
@click.prevent="toggle"
1317
>
@@ -32,7 +36,7 @@ import OnClickOutside from "./OnClickOutside.vue";
3236
import { createPopper } from "@popperjs/core/lib/popper-lite";
3337
import preventOverflow from "@popperjs/core/lib/modifiers/preventOverflow";
3438
import flip from "@popperjs/core/lib/modifiers/flip";
35-
import { ref, watch, onMounted } from "vue";
39+
import { ref, watch, onMounted, inject, computed } from "vue";
3640
3741
const props = defineProps({
3842
placement: {
@@ -86,4 +90,7 @@ onMounted(() => {
8690
});
8791
8892
defineExpose({ hide });
93+
94+
const activeClasses = inject("activeClasses");
95+
8996
</script>

js/Components/HeaderCell.vue

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
class="w-3 h-3 ml-2"
1919
:class="{
2020
'text-gray-400': !cell.sorted,
21-
'text-green-500': cell.sorted,
21+
[activeClasses.text]: cell.sorted,
2222
}"
2323
xmlns="http://www.w3.org/2000/svg"
2424
viewBox="0 0 320 512"
@@ -49,16 +49,21 @@
4949
</template>
5050

5151
<script setup>
52+
53+
import { inject } from "vue";
54+
5255
const props = defineProps({
5356
cell: {
5457
type: Object,
5558
required: true,
5659
},
5760
});
5861
62+
const activeClasses = inject("activeClasses");
63+
5964
function onClick() {
6065
if (props.cell.sortable) {
6166
props.cell.onSort(props.cell.key);
6267
}
6368
}
64-
</script>
69+
</script>

js/Components/Table.vue

Lines changed: 57 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,12 @@
109109
/>
110110
</slot>
111111

112+
<TableActiveFilters
113+
v-if="queryBuilderProps.hasEnabledFilters"
114+
:filters="queryBuilderProps.filters"
115+
:on-filter-change="changeFilterValue"
116+
/>
117+
112118
<slot
113119
name="tableWrapper"
114120
:meta="resourceMeta"
@@ -195,11 +201,12 @@ import HeaderCell from "./HeaderCell.vue";
195201
import TableAddSearchRow from "./TableAddSearchRow.vue";
196202
import TableColumns from "./TableColumns.vue";
197203
import TableFilter from "./TableFilter.vue";
204+
import TableActiveFilters from "./TableActiveFilters.vue";
198205
import TableGlobalSearch from "./TableGlobalSearch.vue";
199206
import TableSearchRows from "./TableSearchRows.vue";
200207
import TableReset from "./TableReset.vue";
201208
import TableWrapper from "./TableWrapper.vue";
202-
import { computed, onMounted, ref, watch, onUnmounted, getCurrentInstance, Transition } from "vue";
209+
import { computed, getCurrentInstance, onMounted, onUnmounted, provide, ref, Transition, watch } from "vue";
203210
import qs from "qs";
204211
import clone from "lodash-es/clone";
205212
import filter from "lodash-es/filter";
@@ -271,8 +278,21 @@ const props = defineProps({
271278
},
272279
required: false,
273280
},
281+
282+
activeClasses: {
283+
type: Object,
284+
required: false,
285+
default() {
286+
return {
287+
text: "text-green-400",
288+
border: "border-green-300"
289+
};
290+
}
291+
}
274292
});
275293
294+
provide("activeClasses", props.activeClasses);
295+
276296
const app = getCurrentInstance();
277297
const $inertia = app ? app.appContext.config.globalProperties.$inertia : props.inertia;
278298
@@ -290,7 +310,7 @@ const queryBuilderProps = computed(() => {
290310
291311
const queryBuilderData = ref(queryBuilderProps.value);
292312
293-
const pageName = computed(() =>{
313+
const pageName = computed(() => {
294314
return queryBuilderProps.value.pageName;
295315
});
296316
@@ -299,19 +319,19 @@ const forcedVisibleSearchInputs = ref([]);
299319
const tableFieldset = ref(null);
300320
301321
const hasOnlyData = computed(() => {
302-
if(queryBuilderProps.value.hasToggleableColumns) {
322+
if (queryBuilderProps.value.hasToggleableColumns) {
303323
return false;
304324
}
305325
306-
if(queryBuilderProps.value.hasFilters) {
326+
if (queryBuilderProps.value.hasFilters) {
307327
return false;
308328
}
309329
310-
if(queryBuilderProps.value.hasSearchInputs) {
330+
if (queryBuilderProps.value.hasSearchInputs) {
311331
return false;
312332
}
313333
314-
if(queryBuilderProps.value.globalSearch) {
334+
if (queryBuilderProps.value.globalSearch) {
315335
return false;
316336
}
317337
@@ -320,26 +340,26 @@ const hasOnlyData = computed(() => {
320340
});
321341
322342
const resourceData = computed(() => {
323-
if(Object.keys(props.resource).length === 0){
343+
if (Object.keys(props.resource).length === 0) {
324344
return props.data;
325345
}
326346
327-
if("data" in props.resource) {
347+
if ("data" in props.resource) {
328348
return props.resource.data;
329349
}
330350
331351
return props.resource;
332352
});
333353
334354
const resourceMeta = computed(() => {
335-
if(Object.keys(props.resource).length === 0){
355+
if (Object.keys(props.resource).length === 0) {
336356
return props.meta;
337357
}
338358
339-
if("links" in props.resource && "meta" in props.resource) {
340-
if(Object.keys(props.resource.links).length === 4
341-
&& "next" in props.resource.links
342-
&& "prev" in props.resource.links) {
359+
if ("links" in props.resource && "meta" in props.resource) {
360+
if (Object.keys(props.resource.links).length === 4
361+
&& "next" in props.resource.links
362+
&& "prev" in props.resource.links) {
343363
return {
344364
...props.resource.meta,
345365
next_page_url: props.resource.links.next,
@@ -348,19 +368,19 @@ const resourceMeta = computed(() => {
348368
}
349369
}
350370
351-
if("meta" in props.resource) {
371+
if ("meta" in props.resource) {
352372
return props.resource.meta;
353373
}
354374
355375
return props.resource;
356376
});
357377
358378
const hasData = computed(() => {
359-
if(resourceData.value.length > 0){
379+
if (resourceData.value.length > 0) {
360380
return true;
361381
}
362382
363-
if(resourceMeta.value.total > 0) {
383+
if (resourceMeta.value.total > 0) {
364384
return true;
365385
}
366386
@@ -380,15 +400,15 @@ function showSearchInput(key) {
380400
}
381401
382402
const canBeReset = computed(() => {
383-
if(forcedVisibleSearchInputs.value.length > 0){
403+
if (forcedVisibleSearchInputs.value.length > 0) {
384404
return true;
385405
}
386406
387407
const queryStringData = qs.parse(location.search.substring(1));
388408
389409
const page = queryStringData[pageName.value];
390410
391-
if(page > 1) {
411+
if (page > 1) {
392412
return true;
393413
}
394414
@@ -398,11 +418,11 @@ const canBeReset = computed(() => {
398418
forEach(["filter", "columns", "cursor", "sort"], (key) => {
399419
const value = queryStringData[prefix + key];
400420
401-
if(key === "sort" && value === queryBuilderProps.value.defaultSort) {
421+
if (key === "sort" && value === queryBuilderProps.value.defaultSort) {
402422
return;
403423
}
404424
405-
if(value !== undefined) {
425+
if (value !== undefined) {
406426
dirty = true;
407427
}
408428
});
@@ -438,7 +458,7 @@ function changeSearchInputValue(key, value) {
438458
clearTimeout(debounceTimeouts[key]);
439459
440460
debounceTimeouts[key] = setTimeout(() => {
441-
if(visitCancelToken.value && props.preventOverlappingRequests){
461+
if (visitCancelToken.value && props.preventOverlappingRequests) {
442462
visitCancelToken.value.cancel();
443463
}
444464
@@ -509,7 +529,7 @@ function getColumnsForQuery() {
509529
return column.key;
510530
}).sort();
511531
512-
if (isEqual(visibleColumnKeys, queryBuilderProps.value.defaultVisibleToggleableColumns)){
532+
if (isEqual(visibleColumnKeys, queryBuilderProps.value.defaultVisibleToggleableColumns)) {
513533
return {};
514534
}
515535
@@ -522,11 +542,11 @@ function dataForNewQueryString() {
522542
523543
const queryData = {};
524544
525-
if(Object.keys(filterForQuery).length > 0) {
545+
if (Object.keys(filterForQuery).length > 0) {
526546
queryData.filter = filterForQuery;
527547
}
528548
529-
if(Object.keys(columnsForQuery).length > 0) {
549+
if (Object.keys(columnsForQuery).length > 0) {
530550
queryData.columns = columnsForQuery;
531551
}
532552
@@ -535,20 +555,20 @@ function dataForNewQueryString() {
535555
const sort = queryBuilderData.value.sort;
536556
const perPage = queryBuilderData.value.perPage;
537557
538-
if(cursor) {
558+
if (cursor) {
539559
queryData.cursor = cursor;
540560
}
541561
542-
if(page > 1) {
562+
if (page > 1) {
543563
queryData.page = page;
544564
}
545565
546-
if(perPage > 1) {
566+
if (perPage > 1) {
547567
queryData.perPage = perPage;
548568
}
549569
550570
551-
if(sort) {
571+
if (sort) {
552572
queryData.sort = sort;
553573
}
554574
@@ -566,10 +586,10 @@ function generateNewQueryString() {
566586
567587
delete queryStringData[pageName.value];
568588
569-
forEach(dataForNewQueryString(), (value, key) =>{
570-
if(key === "page") {
589+
forEach(dataForNewQueryString(), (value, key) => {
590+
if (key === "page") {
571591
queryStringData[pageName.value] = value;
572-
} else if(key === "perPage") {
592+
} else if (key === "perPage") {
573593
queryStringData.perPage = value;
574594
} else {
575595
queryStringData[prefix + key] = value;
@@ -600,7 +620,7 @@ const isVisiting = ref(false);
600620
const visitCancelToken = ref(null);
601621
602622
function visit(url) {
603-
if(!url) {
623+
if (!url) {
604624
return;
605625
}
606626
@@ -611,7 +631,7 @@ function visit(url) {
611631
replace: true,
612632
preserveState: true,
613633
preserveScroll: props.preserveScroll !== false,
614-
onBefore(){
634+
onBefore() {
615635
isVisiting.value = true;
616636
},
617637
onCancelToken(cancelToken) {
@@ -621,12 +641,12 @@ function visit(url) {
621641
isVisiting.value = false;
622642
},
623643
onSuccess() {
624-
if("queryBuilderProps" in $inertia.page.props){
644+
if ("queryBuilderProps" in $inertia.page.props) {
625645
queryBuilderData.value.cursor = queryBuilderProps.value.cursor;
626646
queryBuilderData.value.page = queryBuilderProps.value.page;
627647
}
628648
629-
if(props.preserveScroll === "table-top") {
649+
if (props.preserveScroll === "table-top") {
630650
const offset = -8;
631651
const top = tableFieldset.value.getBoundingClientRect().top + window.pageYOffset + offset;
632652
@@ -640,7 +660,7 @@ function visit(url) {
640660
}
641661
642662
watch(queryBuilderData, () => {
643-
visit(location.pathname + "?" + generateNewQueryString());
663+
visit(location.pathname + "?" + generateNewQueryString());
644664
}, { deep: true });
645665
646666
const inertiaListener = () => {
@@ -658,7 +678,7 @@ onUnmounted(() => {
658678
//
659679
660680
function sortBy(column) {
661-
if(queryBuilderData.value.sort == column) {
681+
if (queryBuilderData.value.sort == column) {
662682
queryBuilderData.value.sort = `-${column}`;
663683
} else {
664684
queryBuilderData.value.sort = column;

0 commit comments

Comments
 (0)