Skip to content

Commit cbb6212

Browse files
authored
Merge pull request #4 from faasdoria/main
Infinite scroll table
2 parents 7473c6a + b112632 commit cbb6212

23 files changed

+285
-109
lines changed

README.md

+11
Original file line numberDiff line numberDiff line change
@@ -96,4 +96,15 @@ Note that we have developed several other open source plugins for Sylius, whose
9696
1. Go to /admin/bulk-edit/products
9797

9898

99+
## Enable/Disable infinite scroll
100+
Use env variable `ASDORIA_BULK_EDIT_INFINITE_SCROLL`
99101

102+
Enable :
103+
```yaml
104+
ASDORIA_BULK_EDIT_INFINITE_SCROLL=1
105+
```
106+
107+
Disable :
108+
```yaml
109+
ASDORIA_BULK_EDIT_INFINITE_SCROLL=0
110+
```

config/config.yaml

+20
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
parameters:
22
env(ASDORIA_MESSENGER_TRANSPORT_BULK_EDIT_DSN): 'doctrine://default'
33
env(ASDORIA_MESSENGER_TRANSPORT_BULK_EDIT_FAILED_DSN): 'doctrine://default?queue_name=asdoria_bulk_edit_failed'
4+
env(ASDORIA_BULK_EDIT_INFINITE_SCROLL): 0
45
asdoria_messenger_transport_bulk_edit_dsn: '%env(resolve:ASDORIA_MESSENGER_TRANSPORT_BULK_EDIT_DSN)%'
56
asdoria_messenger_transport_bulk_edit_failed_dsn: '%env(resolve:ASDORIA_MESSENGER_TRANSPORT_BULK_EDIT_FAILED_DSN)%'
7+
asdoria_bulk_edit_infinite_scroll: '%env(resolve:bool:ASDORIA_BULK_EDIT_INFINITE_SCROLL)%'
8+
69
imports:
710
- { resource: "grids/*.yaml" }
811

12+
twig:
13+
globals:
14+
asdoria_bulk_edit_infinite_scroll: '%asdoria_bulk_edit_infinite_scroll%'
15+
916
sylius_ui:
1017
events:
1118
asdoria_bulk_edit.admin.index.javascripts:
@@ -18,6 +25,19 @@ sylius_ui:
1825
scripts:
1926
template: "@AsdoriaSyliusBulkEditPlugin/Admin/_stylesheets.html.twig"
2027
priority: 10
28+
asdoria_bulk_edit.grid:
29+
blocks:
30+
content:
31+
template: "@AsdoriaSyliusBulkEditPlugin/Admin/Grid/_content.html.twig"
32+
priority: 10
33+
asdoria_bulk_edit.grid.body:
34+
blocks:
35+
navigation:
36+
template: "@AsdoriaSyliusBulkEditPlugin/Admin/Grid/Body/_navigation.html.twig"
37+
priority: 30
38+
table:
39+
template: "@SyliusUi/Grid/Body/_table.html.twig"
40+
priority: 20
2141

2242
framework:
2343
assets:
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
asdoria_bulk_edit_admin_ajax_customer_index:
2+
path: /customers
3+
methods: [GET]
4+
defaults:
5+
_controller: sylius.controller.customer::indexAction
6+
_sylius:
7+
template: "@AsdoriaSyliusBulkEditPlugin/Admin/Grid/Body/_rows.html.twig"
8+
grid: asdoria_bulk_edit_admin_customer
9+
options:
10+
expose: true

config/routes/admin/ajax/product.yaml

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
asdoria_bulk_edit_admin_ajax_product_index:
2+
path: /products
3+
methods: [GET]
4+
defaults:
5+
_controller: sylius.controller.product::indexAction
6+
_sylius:
7+
template: "@AsdoriaSyliusBulkEditPlugin/Admin/Grid/Body/_rows.html.twig"
8+
grid: asdoria_bulk_edit_admin_product
9+
options:
10+
expose: true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
asdoria_bulk_edit_admin_ajax_product_variant_index:
2+
path: /product-variants
3+
methods: [GET]
4+
defaults:
5+
_controller: sylius.controller.product_variant::indexAction
6+
_sylius:
7+
template: "@AsdoriaSyliusBulkEditPlugin/Admin/Grid/Body/_rows.html.twig"
8+
grid: asdoria_bulk_edit_admin_product_variant
9+
options:
10+
expose: true

config/routes/admin/ajax/taxon.yaml

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
asdoria_bulk_edit_admin_ajax_taxon_index:
2+
path: /taxons
3+
methods: [GET]
4+
defaults:
5+
_controller: sylius.controller.taxon::indexAction
6+
_sylius:
7+
template: "@AsdoriaSyliusBulkEditPlugin/Admin/Grid/Body/_rows.html.twig"
8+
grid: asdoria_bulk_edit_admin_taxon
9+
options:
10+
expose: true

config/routes/admin/customer.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ asdoria_bulk_edit_admin_customer_index:
1111
template: '@AsdoriaSyliusBulkEditPlugin/Admin/BulkEdit/index.html.twig'
1212
vars:
1313
icon: list alternate outline
14+
scroll_route: asdoria_bulk_edit_admin_ajax_customer_index

config/routes/admin/product.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ asdoria_bulk_edit_admin_product_index:
1111
template: '@AsdoriaSyliusBulkEditPlugin/Admin/BulkEdit/index.html.twig'
1212
vars:
1313
icon: list alternate outline
14+
scroll_route: asdoria_bulk_edit_admin_ajax_product_index

config/routes/admin/product_variant.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ asdoria_bulk_edit_admin_product_variant_index:
1111
template: '@AsdoriaSyliusBulkEditPlugin/Admin/BulkEdit/index.html.twig'
1212
vars:
1313
icon: outdent
14+
scroll_route: asdoria_bulk_edit_admin_ajax_product_variant_index

config/routes/admin/taxon.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ asdoria_bulk_edit_admin_taxon_index:
1111
template: '@AsdoriaSyliusBulkEditPlugin/Admin/BulkEdit/index.html.twig'
1212
vars:
1313
icon: list alternate outline
14+
scroll_route: asdoria_bulk_edit_admin_ajax_taxon_index

private/css/admin-bulk-edit.scss

+19
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,22 @@
66
.sylius-grid-table-wrapper > table {
77
overflow: hidden;
88
}
9+
10+
.Loader-block {
11+
background-color: #e3e3e3;
12+
background: linear-gradient(
13+
100deg,
14+
rgba(255, 255, 255, 0) 40%,
15+
rgba(255, 255, 255, .5) 50%,
16+
rgba(255, 255, 255, 0) 60%
17+
) #e3e3e3;
18+
background-size: 200% 100%;
19+
background-position-x: 180%;
20+
animation: 1s bg-loading ease-in-out infinite;
21+
}
22+
23+
@keyframes bg-loading {
24+
to {
25+
background-position-x: -20%;
26+
}
27+
}

private/js/Infinite-scroll.js

-75
This file was deleted.

private/js/admin-bulk-edit.js

+34-31
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,67 @@
1-
import $ from 'jquery';
2-
import './sylius/sylius-check-all';
1+
import $ from 'jquery'
2+
import './sylius/sylius-check-all'
33
import 'fomantic-ui/dist/components/transition'
4-
import loadSteps from './load-steps';
5-
import selectResources from './select-resources';
4+
import infiniteScrollTable from './infinite-scroll-table'
5+
import loadSteps from './load-steps'
6+
import selectResources from './select-resources'
67
import axios from 'axios'
78

9+
810
document.addEventListener('DOMContentLoaded', async () => {
9-
$('[data-js-bulk-checkboxes]').checkAll();
11+
$('[data-js-bulk-checkboxes]').checkAll()
1012
$('.ui.dropdown.search:not(.sylius-autocomplete)').dropdown({
1113
action: 'hide',
1214
onChange: function (value, text, $selectedItem) {
1315
changeInputValue()
14-
selectedInputValue().value = '';
16+
selectedInputValue().value = ''
1517
}
1618
})
1719

20+
infiniteScrollTable()
1821
loadSteps()
1922
selectResources()
2023
changeInputValue(true)
21-
});
24+
})
2225

23-
const PREFIX_INPUT = 'criteria_asdoria_bulk_edit_search_attribute_value'
24-
const selectedInputValue = () => document.querySelector(`#${PREFIX_INPUT}_value`)
25-
const selectedInputLocale = () => document.querySelector(`#${PREFIX_INPUT}_localeCode`)
26-
const selectedInputAttribute = () => document.querySelector(`#${PREFIX_INPUT}_attribute`)
27-
const selectedInputContainer = () => selectedInputValue().closest("div.field")
28-
const initInputValue = (name, el) => {
29-
const queryString = window.location.search;
30-
const urlParams = new URLSearchParams(queryString);
26+
const PREFIX_INPUT = 'criteria_asdoria_bulk_edit_search_attribute_value'
27+
const selectedInputValue = () => document.querySelector(`#${ PREFIX_INPUT }_value`)
28+
const selectedInputLocale = () => document.querySelector(`#${ PREFIX_INPUT }_localeCode`)
29+
const selectedInputAttribute = () => document.querySelector(`#${ PREFIX_INPUT }_attribute`)
30+
const selectedInputContainer = () => selectedInputValue().closest('div.field')
31+
const initInputValue = (name, el) => {
32+
const queryString = window.location.search
33+
const urlParams = new URLSearchParams(queryString)
3134
const initialValue = urlParams.get(name)
3235
if (el.type === 'checkbox') {
33-
el.checked = true
34-
return;
36+
el.checked = true
37+
return
3538
}
3639
el.value = urlParams.get(name)
3740
}
38-
const changeInputValue = (init = false) => {
41+
const changeInputValue = (init = false) => {
3942
const inputLocale = selectedInputLocale()
40-
const inputAttr = selectedInputAttribute()
43+
const inputAttr = selectedInputAttribute()
4144

42-
if (!inputLocale || !inputAttr) return;
45+
if (!inputLocale || !inputAttr) return
4346

44-
const localeCode = inputLocale.value
45-
const {renderUrl} = inputAttr.dataset
46-
const attrValue = inputAttr.value
47-
if (!localeCode || !attrValue) return;
47+
const localeCode = inputLocale.value
48+
const { renderUrl } = inputAttr.dataset
49+
const attrValue = inputAttr.value
50+
if (!localeCode || !attrValue) return
4851

49-
const url = `${renderUrl}?sylius_product_attribute_choice[]=${attrValue}&locale_code=${localeCode}`;
52+
const url = `${ renderUrl }?sylius_product_attribute_choice[]=${ attrValue }&locale_code=${ localeCode }`
5053

5154
axios
5255
.get(url)
53-
.then(({data}) => {
56+
.then(({ data }) => {
5457
const container = selectedInputContainer()
5558
container.classList.remove('error')
56-
container.innerHTML = data;
57-
const input = selectedInputValue()
59+
container.innerHTML = data
60+
const input = selectedInputValue()
5861
if (input) {
59-
const {name} = input.dataset
60-
input.name = name
62+
const { name } = input.dataset
63+
input.name = name
6164
if (init) initInputValue(name, input)
6265
}
63-
});
66+
})
6467
}

private/js/infinite-scroll-table.js

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import axios from 'axios'
2+
import { processObserver } from './utils/intersectionObserver'
3+
4+
class InfiniteScrollTable {
5+
constructor ({ container, loading, maxPages, path }) {
6+
this.container = container
7+
this.loading = loading
8+
this.page = 1
9+
this.path = path
10+
this.maxPages = maxPages
11+
this.tableBody = this.container.querySelector('table > tbody')
12+
}
13+
14+
fetchData () {
15+
axios.get(this.path, { params: { 'page': this.page } })
16+
.then(({ data: DOM }) => {
17+
const tbody = document.createElement('tbody')
18+
tbody.innerHTML = DOM
19+
20+
const children = [...tbody.children]
21+
children.forEach(child => {
22+
this.tableBody.appendChild(child)
23+
})
24+
25+
this.initLoading()
26+
})
27+
.catch(e => {
28+
if (e.response?.status === 404) {
29+
this.loading.style.display = 'none'
30+
}
31+
})
32+
}
33+
34+
initLoading () {
35+
this.loading.style.display = 'block'
36+
37+
const onObservation = () => {
38+
this.page++
39+
if (this.page > this.maxPages) {
40+
this.loading.style.display = 'none'
41+
return
42+
}
43+
this.fetchData()
44+
}
45+
46+
processObserver({ callback: onObservation, elementsObserved: [this.loading] })
47+
}
48+
49+
init () {
50+
this.initLoading()
51+
}
52+
}
53+
54+
export default () => {
55+
const container = document.querySelector('.js-infinite-scroll')
56+
const loading = document.querySelector('.js-infinite-scroll-loading')
57+
if (!container || !loading) return
58+
59+
const path = container.dataset?.path ?? null
60+
if (!path) return
61+
62+
const maxPages = container.dataset?.maxPages ?? null
63+
if (!maxPages) return
64+
65+
const instanceInfiniteScroll = new InfiniteScrollTable({
66+
container,
67+
loading,
68+
path,
69+
maxPages,
70+
})
71+
instanceInfiniteScroll.init()
72+
}

private/js/utils/createElementHTML.js

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export const createElementFromHTML = (htmlString) => {
2+
var div = document.createElement('div');
3+
div.innerHTML = htmlString.trim();
4+
5+
return div.firstChild;
6+
}

0 commit comments

Comments
 (0)