Skip to content

Commit dd260dc

Browse files
Make autoicomplete box and filters responsive
1 parent 62c1c18 commit dd260dc

File tree

14 files changed

+142
-115
lines changed

14 files changed

+142
-115
lines changed

src/Controller/Filter.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ public function viewMore(Request $request, string $filterField): Response
6262
}
6363

6464
$choices = [];
65-
/** @var Taxon $currentTaxon */
66-
$currentTaxon = $this->taxonRepository->find($request->get('taxon'));
65+
/** @var ?Taxon $currentTaxon */
66+
$currentTaxon = $request->get('taxon') ? $this->taxonRepository->find($request->get('taxon')) : null;
6767
/** @var ChannelInterface $currentChannel */
6868
$currentChannel = $this->channelContext->getChannel();
6969
$currentLocaleCode = $this->localeContext->getLocaleCode();
@@ -86,7 +86,7 @@ public function viewMore(Request $request, string $filterField): Response
8686
['sku', 'source'],
8787
1,
8888
0,
89-
$currentTaxon->getCode(),
89+
$currentTaxon?->getCode(),
9090
$search,
9191
$gallyFilters,
9292
);

src/Controller/Shop/SearchController.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public function __construct(
3434
) {
3535
}
3636

37-
public function getForm(RequestStack $requestStack): Response
37+
public function getForm(Request $renderRequest, RequestStack $requestStack): Response
3838
{
3939
/** @var string $query */
4040
$query = $requestStack->getMainRequest()?->get('query');
@@ -52,7 +52,10 @@ public function getForm(RequestStack $requestStack): Response
5252

5353
return $this->render(
5454
'@GallySyliusPlugin/shop/shared/components/header/search/form.html.twig',
55-
['searchForm' => $searchForm->createView()]
55+
[
56+
'searchForm' => $searchForm->createView(),
57+
'mobileMode' => $renderRequest->get('mobile_mode'),
58+
]
5659
);
5760
}
5861

src/Form/Type/Filter/GallyDynamicFilterType.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ private function buildHasMoreUrl(string $field): string
130130
$parameters = new Parameters($queryParameters);
131131
/** @var array<string, array<string, mixed>> $criteria */
132132
$criteria = $parameters->get('criteria', []);
133-
$search = (isset($criteria['search'], $criteria['search']['value'])) ? $criteria['search']['value'] : '';
133+
$query = $parameters->get('query', null);
134+
$search = $query ?: ((isset($criteria['search'], $criteria['search']['value'])) ? $criteria['search']['value'] : '');
134135
unset($criteria['search']);
135136
/** @var string $slug */
136137
$slug = $request?->attributes->get('slug') ?? '';

src/Resources/config/app/twig_hooks.yml

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
sylius_twig_hooks:
22
hooks:
33
'sylius_shop.base.header.content':
4-
search:
5-
template: '@GallySyliusPlugin/shop/shared/components/header/search/hook.html.twig'
4+
search_bar:
5+
template: '@GallySyliusPlugin/shop/shared/components/header/search/search_bar.html.twig'
66
priority: 250
7+
8+
'sylius_shop.base.header':
9+
search_bar:
10+
template: '@GallySyliusPlugin/shop/shared/components/header/search/search_bar_mobile.html.twig'
11+
priority: 150
712

813
'sylius_shop.product.search.content':
914
breadcrumbs:
@@ -18,10 +23,18 @@ sylius_twig_hooks:
1823
component: ~
1924
props: ~
2025
priority: 350
26+
27+
'sylius_shop.product.index.content.body.main.filters':
28+
filter_button:
29+
template: '@GallySyliusPlugin/shop/product/search/content/body/main/filters/filter_button.html.twig'
30+
priority: 100
2131

2232
'sylius_shop.product.search.content.body.main.filters':
2333
search:
2434
template: '@GallySyliusPlugin/shop/product/search/content/body/main/filters/search.html.twig'
35+
filter_button:
36+
template: '@GallySyliusPlugin/shop/product/search/content/body/main/filters/filter_button.html.twig'
37+
priority: 100
2538

2639
'sylius_shop.product.search.content.body.sidebar':
2740
taxonomy:

src/Resources/public/search.js

Lines changed: 86 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,97 +1,93 @@
1-
const gallySearchFormHandler = function (event) {
2-
const gallySearchFormContainer = document.querySelector('#searchFormContainer');
3-
if (null !== gallySearchFormContainer) {
4-
const gallyPreviewUrl = gallySearchFormContainer.dataset.previewUrl;
5-
const gallySearchForm = gallySearchFormContainer.querySelector('form');
6-
const gallySearchInput = gallySearchForm.querySelector('input');
7-
const gallySearchResult = gallySearchFormContainer.querySelector('#collapsedSearchResults');
8-
9-
let abortController = null;
10-
const gallySearchResultCollapsible = bootstrap.Collapse.getOrCreateInstance(gallySearchResult, {
11-
toggle: false
12-
});
13-
14-
gallySearchInput.addEventListener('input', (event) => {
15-
const queryText = event.target.value;
16-
17-
if (queryText.length >= 3) {
18-
let formData = new FormData(gallySearchForm);
19-
const plainFormData = Object.fromEntries(formData.entries());
20-
21-
let formDataArray = [];
22-
for (const [key, value] of Object.entries(plainFormData)) {
23-
formDataArray.push(`${encodeURIComponent(key)}=${value}`);
24-
}
25-
let formDataString = formDataArray.join('&');
26-
27-
gallySearchResult.querySelector('.loading-results').classList.remove('d-none')
28-
gallySearchResult.querySelector('.results').classList.add('d-none')
29-
gallySearchResult.querySelector('.results').textContent = '';
30-
gallySearchResultCollapsible.show();
31-
32-
if (null !== abortController) {
33-
abortController.abort();
34-
abortController = null;
35-
}
36-
37-
abortController = new AbortController();
38-
39-
(async () => {
40-
const rawResponse = await fetch(gallyPreviewUrl, {
41-
method: 'POST',
42-
headers: {
43-
'Content-Type': 'application/x-www-form-urlencoded'
44-
},
45-
body: formDataString,
46-
signal: abortController.signal
47-
}).catch((error) => {
48-
// If the request was aborted, do nothing
49-
if (error.name === 'AbortError') {
50-
return;
51-
}
52-
53-
// Otherwise, handle the error here or throw it back to the console
54-
throw error;
55-
});
56-
57-
// On aborted calls, rawResponse will be undefined
58-
if (rawResponse !== undefined) {
59-
const content = await rawResponse.json();
60-
// Do something with the results here
61-
62-
gallySearchResult.querySelector('.loading-results').classList.add('d-none');
63-
gallySearchResult.querySelector('.results').classList.remove('d-none');
64-
gallySearchResult.querySelector('.results').innerHTML = content.htmlResults;
65-
}
66-
})();
67-
} else {
68-
gallySearchResultCollapsible.hide();
1+
const gallySearchFormHandler = function () {
2+
const gallySearchFormContainers = document.querySelectorAll('.searchFormContainer');
3+
4+
gallySearchFormContainers.forEach(container => {
5+
const gallyPreviewUrl = container.dataset.previewUrl;
6+
const gallySearchForm = container.querySelector('form');
7+
const gallySearchInput = gallySearchForm.querySelector('input');
8+
const gallySearchResult = container.querySelector('.collapsedSearchResults');
9+
10+
let abortController = null;
11+
12+
gallySearchInput.addEventListener('input', (event) => {
13+
const queryText = event.target.value;
14+
15+
if (queryText.length >= 3) {
16+
const formData = new FormData(gallySearchForm);
17+
const plainFormData = Object.fromEntries(formData.entries());
18+
const formDataString = new URLSearchParams(plainFormData).toString();
19+
20+
gallySearchResult.querySelector('.loading-results').classList.remove('d-none');
21+
gallySearchResult.querySelector('.results').classList.add('d-none');
22+
gallySearchResult.querySelector('.results').textContent = '';
23+
gallySearchResult.classList.add('show');
24+
25+
if (abortController) {
26+
abortController.abort();
27+
}
28+
29+
abortController = new AbortController();
30+
31+
(async () => {
32+
try {
33+
const rawResponse = await fetch(gallyPreviewUrl, {
34+
method: 'POST',
35+
headers: {
36+
'Content-Type': 'application/x-www-form-urlencoded'
37+
},
38+
body: formDataString,
39+
signal: abortController.signal
40+
});
41+
42+
const content = await rawResponse.json();
43+
44+
gallySearchResult.querySelector('.loading-results').classList.add('d-none');
45+
gallySearchResult.querySelector('.results').classList.remove('d-none');
46+
gallySearchResult.querySelector('.results').innerHTML = content.htmlResults;
47+
48+
if (!content.htmlResults) {
49+
gallySearchResult.classList.remove('show');
6950
}
70-
});
71-
72-
//
73-
gallySearchInput.addEventListener('focus', (event) => {
74-
const queryText = event.target.value;
75-
if (queryText.length >= 3) {
76-
// If there are already some results, display them
77-
if (gallySearchResult.querySelector('.loading-results').classList.contains('d-none')) {
78-
gallySearchResultCollapsible.show();
79-
}
80-
// Otherwise, trigger a search
81-
else {
82-
gallySearchInput.dispatchEvent(new Event('input'));
83-
}
51+
52+
if (gallySearchResult.querySelector('.results .products')) {
53+
console.log('has product');
54+
gallySearchResult.parentElement.classList.add('start-0');
55+
gallySearchResult.parentElement.style.width = '100%';
56+
} else {
57+
console.log('no has product');
58+
gallySearchResult.parentElement.classList.remove('start-0');
59+
gallySearchResult.parentElement.style.width = 'auto';
8460
}
85-
});
8661

87-
// Add a listener to close the searchResult container
88-
document.addEventListener('click', function (event) {
89-
const gallySearchResult = event.target.closest('#collapsedSearchResults');
90-
if (null === gallySearchResult) {
91-
gallySearchResultCollapsible.hide();
62+
} catch (error) {
63+
if (error.name !== 'AbortError') {
64+
console.error(error);
9265
}
93-
});
66+
}
67+
})();
68+
} else {
69+
gallySearchResult.classList.remove('show');
70+
}
71+
});
72+
73+
gallySearchInput.addEventListener('focus', (event) => {
74+
const queryText = event.target.value;
75+
if (queryText.length >= 3) {
76+
if (gallySearchResult.querySelector('.results').innerHTML.trim() !== '') {
77+
gallySearchResult.classList.add('show');
78+
} else {
79+
gallySearchInput.dispatchEvent(new Event('input'));
80+
}
81+
}
82+
});
83+
});
84+
85+
// Close when clicking outside
86+
document.addEventListener('click', function (event) {
87+
if (!event.target.closest('.collapsedSearchResults') && !event.target.closest('.searchFormContainer')) {
88+
document.querySelectorAll('.collapsedSearchResults').forEach(element => element.classList.remove('show'));
9489
}
95-
}
90+
});
91+
};
9692

9793
window.addEventListener("DOMContentLoaded", gallySearchFormHandler);

src/Resources/views/shop/product/index/content/body/sidebar/filters.html.twig

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
{% import '@SyliusShop/shared/buttons.html.twig' as buttons %}
22
{% if is_gally_enabled() %}
33
{% set products = hookable_metadata.context.products %}
4-
<div {{ sylius_test_html_attribute('vertical-filters') }}>
5-
<div class="d-flex flex-column">
4+
<div {{ sylius_test_html_attribute('vertical-filters') }} class="offcanvas-lg offcanvas-start offcanvas-wide p-0" id="searchFilters">
5+
<div class="d-lg-none position-absolute end-0 p-2">
6+
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#searchFilters" aria-label="Close"></button>
7+
</div>
8+
<div class="d-flex flex-column overflow-auto my-2 my-lg-2 px-4 px-lg-2">
69
<form method="get" action="{{ path('sylius_shop_product_index', {'slug': app.request.attributes.get('slug')}) }}">
710
{# Keep the current search in the query on filtering #}
811
{% if products.parameters.get('criteria').search is defined %}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<div class="col-auto d-lg-none">
2+
<button class="btn btn-icon btn-outline-secondary btn-sm" type="button" data-bs-toggle="offcanvas" data-bs-target="#searchFilters" aria-controls="searchFilters" aria-expanded="false" aria-label="Toggle filter">
3+
{{ ux_icon('tabler:filter') }}
4+
</button>
5+
</div>

src/Resources/views/shop/product/search/content/body/sidebar/filters.html.twig

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22
{% if is_gally_enabled() %}
33
{% set products = hookable_metadata.context.products %}
44
{% set query = products.parameters.get('query')|default(products.parameters.get('criteria').search.value|default(false)) %}
5-
<div {{ sylius_test_html_attribute('vertical-filters') }}>
6-
<div class="d-flex flex-column">
5+
<div {{ sylius_test_html_attribute('vertical-filters') }} class="offcanvas-lg offcanvas-start offcanvas-wide p-0" id="searchFilters">
6+
<div class="d-lg-none position-absolute end-0 p-2">
7+
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#searchFilters" aria-label="Close"></button>
8+
</div>
9+
<div class="d-flex flex-column overflow-auto my-2 my-lg-2 px-4 px-lg-2">
710
<form method="get" action="{{ path('gally_search_result_page') }}">
811
{# Keep the current search in the query on filtering #}
912
{% if query %}

src/Resources/views/shop/shared/components/header/search/autocomplete/results.html.twig

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
{% import "@SyliusShop/shared/macro/money.html.twig" as money %}
2+
{% if categories|length or attributes|length or products|length %}
23
<div class="row">
34
{% if categories|length or attributes|length %}
45
{% if products|length %}
5-
<div class="col-md-3 pe-2 border-end">
6+
<div class="col-md-3 pe-2">
67
{% else %}
78
<div class="col-md-12 pe-2">
89
{% endif %}
@@ -43,6 +44,8 @@
4344
<small>{{ option_label }}</small>
4445
</a>
4546
{% endfor %}
47+
48+
<hr class="d-lg-none"/>
4649
{% endif %}
4750
</div>
4851

@@ -51,7 +54,7 @@
5154
<div class="col-md-12">
5255
{% endif %}
5356
{% if products|length %}
54-
<div class="row">
57+
<div class="row products">
5558
<h6 class="mb-3">{{ 'gally_sylius.autocomplete.products_title'|trans() }}</h6>
5659
{% for product in products %}
5760
{% set product_name = product['name'] %}
@@ -74,4 +77,4 @@
7477
{% endif %}
7578
</div>
7679
</div>
77-
80+
{% endif %}
Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
1-
<div class="col" id="searchFormContainer" data-preview-url="{{ path('gally_search_result_preview_ajax') }}">
1+
<div class="searchFormContainer col pt-1 {{ mobileMode ? 'd-lg-none' : 'd-none d-lg-block' }}" data-preview-url="{{ path('gally_search_result_preview_ajax') }}">
22
{{ form_start(searchForm) }}
3-
<div class="input-group mb-1 accordion-header">
3+
<div class="input-group mb-2 mt-2 mt-lg-0 mb-lg-1 px-2 px-lg-0">
44
{{ form_widget(searchForm.query) }}
55
<button type="submit" id="search" class="btn btn-outline-secondary btn-sm collapsed" data-bs-toggle="collapse" data-bs-target="#collapsedSearchResults" aria-expanded="false" aria-controls="collapsedSearchResults">
66
{{ ux_icon('tabler:search') }}
77
</button>
8-
98
</div>
10-
<div id="collapsedSearchResults" class="collapse" style="width: 930px; position: absolute; z-index: 10000; margin-left: -250px">
11-
<div class="card p-3">
12-
<div class="loading-results text-center">
9+
{{ form_end(searchForm) }}
10+
<div class="position-absolute d-flex justify-content-center" style="z-index: 10000">
11+
<div class="card collapsedSearchResults collapse mx-2 mx-lg-0 p-3" style="max-width: 930px">
12+
<div class="loading-results text-center d-none">
1313
<div class="spinner-border" role="status">
1414
<span class="visually-hidden">Loading...</span>
1515
</div>
1616
</div>
17-
<div class="results d-none">
17+
<div class="results">
1818
</div>
1919
</div>
2020
</div>
21-
{{ form_end(searchForm) }}
2221
</div>

0 commit comments

Comments
 (0)