From 370a056ce13add695e5e2339da15d088739eb429 Mon Sep 17 00:00:00 2001 From: Nikita Zhavoronkov Date: Tue, 24 Oct 2017 18:17:46 +0300 Subject: [PATCH] Add ajax page update, seo urls, price slider --- Block/AjaxScript.php | 41 +++ Block/LayeredNavigation/RenderLayered.php | 10 +- Block/LayeredNavigation/SliderRenderer.php | 53 ++++ Controller/Router.php | 84 ++++++ Model/Layer/Filter/Attribute.php | 64 +---- Model/Layer/Filter/Category.php | 68 ++--- Model/Layer/Filter/Decimal.php | 86 ++++-- Model/Layer/Filter/Item.php | 30 +- Model/Layer/Filter/Price.php | 94 ++++--- Model/Layer/Filter/SliderTrait.php | 85 ++++++ Model/ResourceModel/Fulltext/Collection.php | 46 +++- Model/Url/Builder.php | 169 ++++++++++++ Model/Url/Hydrator.php | 257 ++++++++++++++++++ Model/Url/Translit.php | 20 ++ Plugin/CategoryView.php | 19 ++ Plugin/CategoryViewBlock.php | 26 ++ Plugin/FilterRenderer.php | 50 +++- Plugin/State.php | 27 ++ composer.json | 2 +- etc/adminhtml/system.xml | 34 +++ etc/config.xml | 18 ++ etc/di.xml | 64 ++++- etc/frontend/di.xml | 14 + etc/module.xml | 2 +- .../frontend/layout/catalog_category_view.xml | 18 ++ view/frontend/requirejs-config.js | 8 + view/frontend/templates/ajax.phtml | 8 + view/frontend/templates/slider.phtml | 21 ++ view/frontend/web/js/navigation.js | 138 ++++++++++ view/frontend/web/js/slider.js | 51 ++++ view/frontend/web/styles.css | 61 +++++ 31 files changed, 1477 insertions(+), 191 deletions(-) create mode 100644 Block/AjaxScript.php create mode 100644 Block/LayeredNavigation/SliderRenderer.php create mode 100644 Controller/Router.php create mode 100644 Model/Layer/Filter/SliderTrait.php create mode 100644 Model/Url/Builder.php create mode 100644 Model/Url/Hydrator.php create mode 100644 Model/Url/Translit.php create mode 100644 Plugin/CategoryView.php create mode 100644 Plugin/CategoryViewBlock.php create mode 100644 Plugin/State.php create mode 100755 etc/adminhtml/system.xml create mode 100755 etc/config.xml create mode 100644 etc/frontend/di.xml create mode 100755 view/frontend/layout/catalog_category_view.xml create mode 100755 view/frontend/requirejs-config.js create mode 100644 view/frontend/templates/ajax.phtml create mode 100644 view/frontend/templates/slider.phtml create mode 100644 view/frontend/web/js/navigation.js create mode 100644 view/frontend/web/js/slider.js create mode 100644 view/frontend/web/styles.css diff --git a/Block/AjaxScript.php b/Block/AjaxScript.php new file mode 100644 index 0000000..bd0dd61 --- /dev/null +++ b/Block/AjaxScript.php @@ -0,0 +1,41 @@ + !$this->getIsAjax(), + 'filtersContainer' => '#layered-filter-block', + 'productsContainer' => '.' . \Niks\LayeredNavigation\Plugin\CategoryViewBlock::PRODUCT_LIST_WRAPPER + ]; + return json_encode($config); + } + + /** + * Get ajax option + * + * @return string + */ + protected function getIsAjax() + { + return $this->_scopeConfig->getValue( + 'niks_layered_navigation/general/ajax', + \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + $this->_storeManager->getStore()->getId() + ); + } +} diff --git a/Block/LayeredNavigation/RenderLayered.php b/Block/LayeredNavigation/RenderLayered.php index 2a8c1d4..75d7869 100644 --- a/Block/LayeredNavigation/RenderLayered.php +++ b/Block/LayeredNavigation/RenderLayered.php @@ -17,10 +17,10 @@ class RenderLayered extends CoreRender */ public function buildUrl($attributeCode, $optionId) { - $value = $this->filter->getValueAsArray(); - $value[] = $optionId; - $value = implode('_', $value); - $query = [$attributeCode => $value]; - return $this->_urlBuilder->getUrl('*/*/*', ['_current' => true, '_use_rewrite' => true, '_query' => $query]); + return $this->_urlBuilder->getFilterUrl( + $this->filter->getRequestVar(), + $optionId, + [] + ); } } diff --git a/Block/LayeredNavigation/SliderRenderer.php b/Block/LayeredNavigation/SliderRenderer.php new file mode 100644 index 0000000..834c15f --- /dev/null +++ b/Block/LayeredNavigation/SliderRenderer.php @@ -0,0 +1,53 @@ +getFilter()->getCurrentValue(); + if (isset($currentValues[0])) { + return $currentValues[0]; + } + return $this->getFilter()->getMin(); + } + + public function getTo() + { + $currentValues = $this->getFilter()->getCurrentValue(); + if (isset($currentValues[1])) { + return $currentValues[1]; + } + return $this->getFilter()->getMax(); + } + + public function getPriceRangeUrlTemplate() + { + return $this->_urlBuilder->getFilterUrl( + $this->getFilter()->getRequestVar(), + '{{from}}-{{to}}', + [], + true + ); + } + + public function getCurrencySymbol() + { + return $this->_storeManager->getStore()->getCurrentCurrency()->getCurrencySymbol(); + } +} diff --git a/Controller/Router.php b/Controller/Router.php new file mode 100644 index 0000000..1dfe42a --- /dev/null +++ b/Controller/Router.php @@ -0,0 +1,84 @@ +urlHydrator = $urlHydrator; + $this->registry = $registry; + parent::__construct($actionFactory, $url, $storeManager, $response, $urlFinder); + } + + /** + * Match corresponding navigation URL and modify request + * + * @param \Magento\Framework\App\RequestInterface $request + * @return \Magento\Framework\App\ActionInterface|null + */ + public function match(\Magento\Framework\App\RequestInterface $request) + { + $parentMatch = parent::match($request); + if ($parentMatch !== null) { + $request->setAlias( + Builder::REWRITE_NAVIGATION_PATH_ALIAS, + ltrim($request->getOriginalPathInfo(), '/') + ); + return $parentMatch; + } + + $filterString = '/' . $this->urlHydrator->getFilterString($request->getPathInfo()); + $originalPath = preg_replace('%' . $filterString . '(?!.*' . $filterString . '.*)%', '', $request->getPathInfo()); + + $rewrite = $this->getRewrite($originalPath, $this->storeManager->getStore()->getId()); + if ($rewrite === null) { + return null; + } + if ($rewrite->getRedirectType()) { + return $this->processRedirect($request, $rewrite); + } + + $this->registry->register('current_category_id', $rewrite->getEntityId()); + $filterParams = $this->urlHydrator->extract($request->getPathInfo()); + if (empty($filterParams)) { + return null; + } + $request->setParam('navigation_filters', $filterParams); + $request->setAlias(UrlInterface::REWRITE_REQUEST_PATH_ALIAS, ltrim($request->getPathInfo(), '/')); + $request->setAlias(Builder::REWRITE_NAVIGATION_PATH_ALIAS, $rewrite->getRequestPath()); + $request->setPathInfo('/' . $rewrite->getTargetPath()); + return $this->actionFactory->create(\Magento\Framework\App\Action\Forward::class); + } +} diff --git a/Model/Layer/Filter/Attribute.php b/Model/Layer/Filter/Attribute.php index d1b7630..d1a7a54 100644 --- a/Model/Layer/Filter/Attribute.php +++ b/Model/Layer/Filter/Attribute.php @@ -8,14 +8,14 @@ class Attribute extends CoreAttribute { /** - * @var \Magento\Framework\App\RequestInterface + * @var \Magento\Framework\Filter\StripTags */ - protected $_request; + private $tagFilter; /** - * @var \Magento\Framework\Filter\StripTags + * @var \\Niks\LayeredNavigation\Model\Url\Builder */ - private $tagFilter; + protected $urlBuilder; /** * @var \Magento\CatalogSearch\Model\Layer\Category\ItemCollectionProvider @@ -36,6 +36,7 @@ public function __construct( \Magento\Catalog\Model\Layer $layer, \Magento\Catalog\Model\Layer\Filter\Item\DataBuilder $itemDataBuilder, \Magento\Framework\Filter\StripTags $tagFilter, + \Niks\LayeredNavigation\Model\Url\Builder $urlBuilder, \Magento\CatalogSearch\Model\Layer\Category\ItemCollectionProvider $collectionProvider, array $data = [] ) { @@ -48,17 +49,10 @@ public function __construct( $data ); $this->tagFilter = $tagFilter; + $this->urlBuilder = $urlBuilder; $this->collectionProvider = $collectionProvider; } - /** - * @return \Magento\Framework\App\RequestInterface - */ - protected function _getRequest() - { - return $this->_request; - } - /** * Apply attribute option filter to product collection * @@ -68,8 +62,8 @@ protected function _getRequest() */ public function apply(\Magento\Framework\App\RequestInterface $request) { - $this->_request = $request; - if (empty($request->getParam($this->_requestVar))) { + $values = $this->urlBuilder->getValuesFromUrl($this->_requestVar); + if (!$values) { return $this; } @@ -78,8 +72,7 @@ public function apply(\Magento\Framework\App\RequestInterface $request) ->getProductCollection(); $this->applyToCollection($productCollection); - $attributeValues = $this->getValueAsArray(); - foreach ($attributeValues as $value) { + foreach ($values as $value) { $label = $this->getOptionText($value); $this->getLayer() ->getState() @@ -88,21 +81,6 @@ public function apply(\Magento\Framework\App\RequestInterface $request) return $this; } - /** - * Get filter values - * - * @return array - */ - public function getValueAsArray() - { - $paramValue = $this->_getRequest()->getParam($this->_requestVar); - if (!$paramValue) { - return []; - } - $requestValue = $this->_getRequest()->getParam($this->_requestVar); - return explode('_', $requestValue); - } - /** * Apply current filter to collection * @@ -111,28 +89,13 @@ public function getValueAsArray() public function applyToCollection($collection) { $attribute = $this->getAttributeModel(); - $attributeValue = $this->getValueAsArray(); + $attributeValue = $this->urlBuilder->getValuesFromUrl($this->_requestVar); if (empty($attributeValue)) { return $this; } $collection->addFieldToFilter($attribute->getAttributeCode(), array('in' => $attributeValue)); } - /** - * Get filter value for reset current filter state - * - * @param string $value - * @return string - */ - public function getResetOptionValue($value) - { - $attributeValues = $this->getValueAsArray(); - $key = array_search($value, $attributeValues); - unset($attributeValues[$key]); - return implode('_', $attributeValues); - } - - /** * Get data array for building attribute filter items * @@ -141,7 +104,8 @@ public function getResetOptionValue($value) */ protected function _getItemsData() { - if (!$this->_getRequest()->getParam($this->_requestVar)) { + $values = $this->urlBuilder->getValuesFromUrl($this->_requestVar); + if (!$values) { return parent::_getItemsData(); } @@ -176,9 +140,9 @@ protected function _getItemsData() $options = $attribute->getFrontend() ->getSelectOptions(); - $usedOptions = $this->getValueAsArray(); + foreach ($options as $option) { - if (empty($option['value']) || in_array($option['value'], $usedOptions)) { + if (empty($option['value']) || in_array($option['value'], $values)) { continue; } // Check filter type diff --git a/Model/Layer/Filter/Category.php b/Model/Layer/Filter/Category.php index 69db7fd..40a1e5a 100644 --- a/Model/Layer/Filter/Category.php +++ b/Model/Layer/Filter/Category.php @@ -14,14 +14,14 @@ class Category extends CoreCategory private $escaper; /** - * @var \Magento\Framework\App\RequestInterface + * @var CategoryDataProvider */ - protected $_request; + private $dataProvider; /** - * @var CategoryDataProvider + * @var \\Niks\LayeredNavigation\Model\Url\Builder */ - private $dataProvider; + protected $urlBuilder; /** * @var \Magento\CatalogSearch\Model\Layer\Category\ItemCollectionProvider @@ -45,10 +45,11 @@ public function __construct( \Magento\Catalog\Model\Layer\Filter\Item\DataBuilder $itemDataBuilder, \Magento\Framework\Escaper $escaper, \Magento\Catalog\Model\Layer\Filter\DataProvider\CategoryFactory $categoryDataProviderFactory, + \Niks\LayeredNavigation\Model\Url\Builder $urlBuilder, \Magento\CatalogSearch\Model\Layer\Category\ItemCollectionProvider $collectionProvider, array $data = [] - - ) { + ) + { parent::__construct( $filterItemFactory, $storeManager, @@ -60,17 +61,10 @@ public function __construct( ); $this->escaper = $escaper; $this->dataProvider = $categoryDataProviderFactory->create(['layer' => $this->getLayer()]); + $this->urlBuilder = $urlBuilder; $this->collectionProvider = $collectionProvider; } - /** - * @return \Magento\Framework\App\RequestInterface - */ - protected function _getRequest() - { - return $this->_request; - } - /** * Apply category filter to product collection * @@ -79,8 +73,8 @@ protected function _getRequest() */ public function apply(\Magento\Framework\App\RequestInterface $request) { - $this->_request = $request; - if (empty($request->getParam($this->_requestVar))) { + $values = $this->urlBuilder->getValuesFromUrl($this->_requestVar); + if (!$values) { return $this; } @@ -89,8 +83,6 @@ public function apply(\Magento\Framework\App\RequestInterface $request) ->getProductCollection(); $this->applyToCollection($productCollection); - $values = $this->getValueAsArray(); - /** @var \Magento\Catalog\Model\ResourceModel\Category\Collection $categoryCollection */ $categoryCollection = ObjectManager::getInstance() ->create(\Magento\Catalog\Model\ResourceModel\Category\Collection::class); @@ -116,6 +108,11 @@ public function apply(\Magento\Framework\App\RequestInterface $request) */ protected function _getItemsData() { + $values = $this->urlBuilder->getValuesFromUrl($this->_requestVar); + if (!$values) { + return parent::_getItemsData(); + } + /** @var \Niks\LayeredNavigation\Model\ResourceModel\Fulltext\Collection $productCollection */ $productCollection = $this->getLayer()->getProductCollection(); @@ -134,12 +131,12 @@ protected function _getItemsData() $optionsFacetedData = $collection->getFacetedData('category'); $category = $this->dataProvider->getCategory(); $categories = $category->getChildrenCategories(); - $usedOptions = $this->getValueAsArray(); + if ($category->getIsActive()) { foreach ($categories as $category) { if ($category->getIsActive() && isset($optionsFacetedData[$category->getId()]) - && !in_array($category->getId(), $usedOptions) + && !in_array($category->getId(), $values) ) { $this->itemDataBuilder->addItemData( $this->escaper->escapeHtml($category->getName()), @@ -160,40 +157,11 @@ protected function _getItemsData() */ public function applyToCollection($collection) { - $values = $this->getValueAsArray(); + $values = $this->urlBuilder->getValuesFromUrl($this->_requestVar); if (empty($values)) { return $this; } $collection->addCategoriesFilter(['in' => $values]); return $this; } - - /** - * Get filter values - * - * @return array - */ - public function getValueAsArray() - { - $paramValue = $this->_getRequest()->getParam($this->_requestVar); - if (!$paramValue) { - return []; - } - $requestValue = $this->_getRequest()->getParam($this->_requestVar); - return array_filter(explode('_', $requestValue), function ($value) {return (string)(int)$value === $value;}); - } - - /** - * Get filter value for reset current filter state - * - * @param string $value - * @return string - */ - public function getResetOptionValue($value) - { - $attributeValues = $this->getValueAsArray(); - $key = array_search($value, $attributeValues); - unset($attributeValues[$key]); - return implode('_', $attributeValues); - } } diff --git a/Model/Layer/Filter/Decimal.php b/Model/Layer/Filter/Decimal.php index 07455c8..c1a45ff 100644 --- a/Model/Layer/Filter/Decimal.php +++ b/Model/Layer/Filter/Decimal.php @@ -7,40 +7,54 @@ */ class Decimal extends CoreDecimal { - /** - * @var \Magento\Framework\App\RequestInterface - */ - protected $_request; + use SliderTrait; /** - * @return \Magento\Framework\App\RequestInterface + * @var \\Niks\LayeredNavigation\Model\Url\Builder */ - protected function _getRequest() - { - return $this->_request; - } + protected $urlBuilder; /** - * Apply category filter to product collection - * - * @param \Magento\Framework\App\RequestInterface $request - * @return $this + * @var \Magento\CatalogSearch\Model\Layer\Category\ItemCollectionProvider */ - public function apply(\Magento\Framework\App\RequestInterface $request) + protected $collectionProvider; + + public function __construct( + \Magento\Catalog\Model\Layer\Filter\ItemFactory $filterItemFactory, + \Magento\Store\Model\StoreManagerInterface $storeManager, + \Magento\Catalog\Model\Layer $layer, + \Magento\Catalog\Model\Layer\Filter\Item\DataBuilder $itemDataBuilder, + \Magento\Catalog\Model\ResourceModel\Layer\Filter\DecimalFactory $filterDecimalFactory, + \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency, + \Niks\LayeredNavigation\Model\Url\Builder $urlBuilder, + \Magento\CatalogSearch\Model\Layer\Category\ItemCollectionProvider $collectionProvider, + array $data = []) { - $this->_request = $request; - return parent::apply($request); + parent::__construct( + $filterItemFactory, + $storeManager, + $layer, + $itemDataBuilder, + $filterDecimalFactory, + $priceCurrency, + $data + ); + $this->urlBuilder = $urlBuilder; + $this->collectionProvider = $collectionProvider; } /** * Apply current filter to collection * - * @return Attribute + * @return Decimal */ - public function applyToCollection($collection) + public function applyToCollection($collection, $addFilter = false) { - $request = $this->_getRequest(); - $filter = $request->getParam($this->getRequestVar()); + $values = $this->urlBuilder->getValuesFromUrl($this->_requestVar); + $filter = false; + if ($values) { + $filter = $values[0]; + } if (!$filter || is_array($filter)) { return $this; } @@ -48,10 +62,36 @@ public function applyToCollection($collection) list($from, $to) = explode('-', $filter); $collection->addFieldToFilter( - $this->getAttributeModel()->getAttributeCode(), - ['from' => $from, 'to' => $to] + $this->getAttributeModel()->getAttributeCode(), + ['from' => $from, 'to' => $to] + ); + + if ($addFilter) { + $this->getLayer()->getState()->addFilter( + $this->_createItem($this->renderRangeLabel(empty($from) ? 0 : $from, $to), $filter) ); - + } return $this; } + + public function getCurrentValue() + { + $values = $this->urlBuilder->getValuesFromUrl($this->_requestVar); + $filter = false; + if ($values) { + $filter = $values[0]; + } + $filterParams = explode('-', $filter); + return $filterParams; + } + + public function getMin() + { + return $this->getCollectionWithoutFilter()->getMin($this->_requestVar); + } + + public function getMax() + { + return $this->getCollectionWithoutFilter()->getMax($this->_requestVar); + } } diff --git a/Model/Layer/Filter/Item.php b/Model/Layer/Filter/Item.php index d039a93..a4534fb 100644 --- a/Model/Layer/Filter/Item.php +++ b/Model/Layer/Filter/Item.php @@ -1,7 +1,6 @@ getFilter()->getRequestVar() => $this->getFilter()->getResetOptionValue($this->getValue())]; - $params['_current'] = true; - $params['_use_rewrite'] = true; - $params['_query'] = $query; - $params['_escape'] = true; - return $this->_url->getUrl('*/*/*', $params); + return $this->_url->getRemoveFilterUrl( + $this->getFilter()->getRequestVar(), + $this->getValue(), + [$this->_htmlPagerBlock->getPageVarName() => null] + ); } /** @@ -26,18 +24,10 @@ public function getRemoveUrl() */ public function getUrl() { - $value = $this->getFilter()->getValueAsArray(); - if (empty($value)) { - return parent::getUrl(); - } - $value[] = $this->getValue(); - $value = implode('_', $value); - $query = [ - $this->getFilter()->getRequestVar() => $value, - // exclude current page from urls - $this->_htmlPagerBlock->getPageVarName() => null, - ]; - - return $this->_url->getUrl('*/*/*', ['_current' => true, '_use_rewrite' => true, '_query' => $query]); + return $this->_url->getFilterUrl( + $this->getFilter()->getRequestVar(), + $this->getValue(), + [$this->_htmlPagerBlock->getPageVarName() => null] + ); } } diff --git a/Model/Layer/Filter/Price.php b/Model/Layer/Filter/Price.php index 2b2cfa3..8e6d464 100644 --- a/Model/Layer/Filter/Price.php +++ b/Model/Layer/Filter/Price.php @@ -7,16 +7,23 @@ */ class Price extends CorePrice { - /** - * @var \Magento\Framework\App\RequestInterface - */ - protected $_request; + use SliderTrait; /** * @var \Magento\Catalog\Model\Layer\Filter\DataProvider\Price */ private $dataProvider; + /** + * @var \\Niks\LayeredNavigation\Model\Url\Builder + */ + protected $urlBuilder; + + /** + * @var \Magento\CatalogSearch\Model\Layer\Category\ItemCollectionProvider + */ + protected $collectionProvider; + /** * @param \Magento\Catalog\Model\Layer\Filter\ItemFactory $filterItemFactory * @param \Magento\Store\Model\StoreManagerInterface $storeManager @@ -43,6 +50,8 @@ public function __construct( \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency, \Magento\Catalog\Model\Layer\Filter\Dynamic\AlgorithmFactory $algorithmFactory, \Magento\Catalog\Model\Layer\Filter\DataProvider\PriceFactory $dataProviderFactory, + \Niks\LayeredNavigation\Model\Url\Builder $urlBuilder, + \Magento\CatalogSearch\Model\Layer\Category\ItemCollectionProvider $collectionProvider, array $data = [] ) { @@ -60,26 +69,8 @@ public function __construct( $data ); $this->dataProvider = $dataProviderFactory->create(['layer' => $this->getLayer()]); - } - - /** - * @return \Magento\Framework\App\RequestInterface - */ - protected function _getRequest() - { - return $this->_request; - } - - /** - * Apply category filter to product collection - * - * @param \Magento\Framework\App\RequestInterface $request - * @return $this - */ - public function apply(\Magento\Framework\App\RequestInterface $request) - { - $this->_request = $request; - return parent::apply($request); + $this->urlBuilder = $urlBuilder; + $this->collectionProvider = $collectionProvider; } /** @@ -87,16 +78,16 @@ public function apply(\Magento\Framework\App\RequestInterface $request) * * @return Attribute */ - public function applyToCollection($collection) + public function applyToCollection($collection, $addFilter = false) { - $request = $this->_getRequest(); - $filter = $request->getParam($this->getRequestVar()); - if (!$filter || is_array($filter)) { - return $this; + $values = $this->urlBuilder->getValuesFromUrl($this->_requestVar); + $filter = false; + if ($values) { + $filter = $values[0]; } $filterParams = explode(',', $filter); - $filter = $this->dataProvider->validateFilter($filterParams[0]); + $filter = $this->getCurrentValue(); if (!$filter) { return $this; } @@ -111,9 +102,50 @@ public function applyToCollection($collection) $collection->addFieldToFilter( 'price', - ['from' => $from, 'to' => empty($to) || $from == $to ? $to : $to - self::PRICE_DELTA] + ['from' => $from, 'to' => empty($to) || $from == $to ? $to : $to] ); + if ($addFilter) { + $this->getLayer()->getState()->addFilter( + $this->_createItem($this->_renderRangeLabel(empty($from) ? 0 : $from, $to), $filter) + ); + } return $this; } + + /** + * Get applied values + * + * @return array|bool + */ + public function getCurrentValue() + { + $values = $this->urlBuilder->getValuesFromUrl($this->_requestVar); + $filter = false; + if ($values) { + $filter = $values[0]; + } + $filterParams = explode(',', $filter); + return $this->dataProvider->validateFilter($filterParams[0]); + } + + /** + * Get max value + * + * @return float + */ + public function getMax() + { + return $this->getCollectionWithoutFilter()->getMaxPrice(); + } + + /** + * Get min value + * + * @return float + */ + public function getMin() + { + return $this->getCollectionWithoutFilter()->getMinPrice(); + } } diff --git a/Model/Layer/Filter/SliderTrait.php b/Model/Layer/Filter/SliderTrait.php new file mode 100644 index 0000000..31788b7 --- /dev/null +++ b/Model/Layer/Filter/SliderTrait.php @@ -0,0 +1,85 @@ +applyToCollection($this->getLayer()->getProductCollection(), true); + return $this; + } + + /** + * Get collection without current filter + * + * @return \Niks\LayeredNavigation\Model\ResourceModel\Fulltext\Collection + */ + protected function getCollectionWithoutFilter() + { + if (!$this->_skipFilterCollection) { + /** @var \Niks\LayeredNavigation\Model\ResourceModel\Fulltext\Collection $productCollection */ + $productCollection = $this->getLayer() + ->getProductCollection(); + + /** @var \Niks\LayeredNavigation\Model\ResourceModel\Fulltext\Collection $collection */ + $this->_skipFilterCollection = $this->collectionProvider->getCollection($this->getLayer()->getCurrentCategory()); + $this->_skipFilterCollection->updateSearchCriteriaBuilder(); + $this->getLayer()->prepareProductCollection($this->_skipFilterCollection); + foreach ($productCollection->getAddedFilters() as $field => $condition) { + if ($this->getAttributeModel()->getAttributeCode() == $field) { + continue; + } + $this->_skipFilterCollection->addFieldToFilter($field, $condition); + } + } + return $this->_skipFilterCollection; + } + + /** + * Mock items for slider + * + * @return mixed + */ + protected function _getItemsData() + { + if ($this->isSliderEnabled()) { + $this->itemDataBuilder->addItemData( + true, + true, + true + ); + return $this->itemDataBuilder->build(); + } + return parent::_getItemsData(); + } + + /** + * Check is slider enabled + * + * @return bool + */ + protected function isSliderEnabled() + { + $scope = ObjectManager::getInstance()->get(\Magento\Framework\App\Config\ScopeConfigInterface::class); + $storeManager = ObjectManager::getInstance()->get(StoreManagerInterface::class); + + return $scope->getValue( + 'niks_layered_navigation/general/slider', + \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + $storeManager->getStore()->getId() + ); + } +} \ No newline at end of file diff --git a/Model/ResourceModel/Fulltext/Collection.php b/Model/ResourceModel/Fulltext/Collection.php index 95b184d..8b2f0a4 100644 --- a/Model/ResourceModel/Fulltext/Collection.php +++ b/Model/ResourceModel/Fulltext/Collection.php @@ -58,4 +58,48 @@ public function updateSearchCriteriaBuilder() return $this; } -} \ No newline at end of file + + protected function _prepareStatisticsData() + { + $this->_renderFilters(); + return parent::_prepareStatisticsData(); + } + + + public function getMax($code) + { + $data = $this->prepareDecimalData($code); + return isset($data['max']) ? $data['max'] : false; + } + + public function getMin($code) + { + $data = $this->prepareDecimalData($code); + return isset($data['min']) ? $data['min'] : false; + } + + protected $decimalData = []; + + protected function prepareDecimalData($code) + { + if (!isset($this->decimalData[$code])) { + $this->joinAttribute($code, 'catalog_product/' . $code, 'entity_id'); + $fieldName = $this->_getAttributeFieldName($code); + $sqlEndPart = ') * ' . $this->getCurrencyRate() . ', 2)'; + $select = clone $this->getSelect(); + $select->reset(\Magento\Framework\DB\Select::COLUMNS); + $select->columns( + [ + 'max' => 'ROUND(MAX(' . $fieldName . $sqlEndPart, + 'min' => 'ROUND(MIN(' . $fieldName . $sqlEndPart, + ] + ); + $row = $this->getConnection()->fetchRow($select, $this->_bindParams, \Zend_Db::FETCH_NUM); + $this->decimalData[$code] = [ + 'min' => $row[1], + 'max' => $row[0] + ]; + } + return $this->decimalData[$code]; + } +} diff --git a/Model/Url/Builder.php b/Model/Url/Builder.php new file mode 100644 index 0000000..b413175 --- /dev/null +++ b/Model/Url/Builder.php @@ -0,0 +1,169 @@ +isSeoUrlsEnabled()) { + return parent::_getRoutePath($routeParams); + } + if (!$this->hasData('route_path')) { + $routePath = $this->_getRequest()->getAlias(self::REWRITE_NAVIGATION_PATH_ALIAS); + if (!empty($routeParams['_use_rewrite']) && $routePath !== null && isset($routeParams['_navigation_filters'])) { + if ($routeParams['_navigation_filters']) { + $suffix = $this->getUrlHydrator()->getSuffix(); + $routePath = preg_replace('/' . preg_quote($suffix, '/') . '$/', '', $routePath) . '/' . $routeParams['_navigation_filters'] . $suffix; + } + $this->setData('route_path', $routePath); + return $routePath; + } + } + return parent::_getRoutePath($routeParams); + } + + /** + * Get filter item url + * + * @param string $code + * @param string $value + * @param array $query + * @return string + */ + public function getFilterUrl($code, $value, $query = [], $singleValue = false) + { + $params = ['_current' => true, '_use_rewrite' => true, '_query' => $query]; + $values = []; + if (!$singleValue) { + $values = $this->getValuesFromUrl($code); + } + $values[] = $value; + + if ($this->isSeoUrlsEnabled()) { + $allFilters = $this->_getRequest()->getParam('navigation_filters', []); + $allFilters[$code] = $values; + $filterUrlPart = $this->getUrlHydrator()->hydrate($allFilters); + $params['_navigation_filters'] = $filterUrlPart; + return $this->getUrl('*/*/*', $params); + } + + $values = implode('_', $values); + $params['_query'][$code] = $values; + return $this->getUrl('*/*/*', $params); + } + + /** + * Get remove filter item url + * + * @param string $code + * @param string $value + * @param array $query + * @return string + */ + public function getRemoveFilterUrl($code, $value, $query = []) + { + $params = ['_current' => true, '_use_rewrite' => true, '_query' => $query, '_escape' => true]; + $values = $this->getValuesFromUrl($code); + $key = array_search($value, $values); + unset($values[$key]); + + if ($this->isSeoUrlsEnabled()) { + $allFilters = $this->_getRequest()->getParam('navigation_filters', []); + if (!$values && isset($allFilters[$code])) { + unset($allFilters[$code]); + } else { + $allFilters[$code] = $values; + } + + $filterUrlPart = $this->getUrlHydrator()->hydrate($allFilters); + $params['_navigation_filters'] = $filterUrlPart; + return $this->getUrl('*/*/*', $params); + } + + $params['_query'][$code] = $values ? implode('_', $values) : null; + return $this->getUrl('*/*/*', $params); + } + + /** + * Get array of filter values + * + * @param string $code + * @return array + */ + public function getValuesFromUrl($code) + { + $paramValue = []; + if ($this->isSeoUrlsEnabled()) { + $filters = $this->_getRequest()->getParam('navigation_filters'); + if (is_array($filters) && isset($filters[$code])) { + $paramValue = $filters[$code]; + } + } else { + $paramValue = array_filter(explode('_', $this->_getRequest()->getParam($code))); + } + return $paramValue; + } + + /** + * Chek is seo URLs opyion enabled + * + * @return bool + */ + public function isSeoUrlsEnabled() + { + if ($this->_getRequest()->getModuleName() != 'catalog') { + return false; + } + $storeManager = ObjectManager::getInstance() + ->get(StoreManagerInterface::class); + return $this->_scopeConfig->getValue( + 'niks_layered_navigation/general/friendly_urls', + \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + $storeManager->getStore()->getId() + ); + } + + /** + * Remove ajax option and build url + * + * @param null $routePath + * @param null $routeParams + * @return string + */ + public function getUrl($routePath = null, $routeParams = null) + { + if (isset($routeParams['_query'])) { + $routeParams['_query']['niksAjax'] = null; + } + return parent::getUrl($routePath, $routeParams); + } + + protected function getUrlHydrator() + { + if (!$this->urlHydrator) { + $this->urlHydrator = ObjectManager::getInstance() + ->get(Hydrator::class); + } + return $this->urlHydrator; + } +} diff --git a/Model/Url/Hydrator.php b/Model/Url/Hydrator.php new file mode 100644 index 0000000..e7bded4 --- /dev/null +++ b/Model/Url/Hydrator.php @@ -0,0 +1,257 @@ +_attrOptionCollectionFactory = $attrOptionCollectionFactory; + $this->categoryCollectionFactory = $categoryCollectionFactory; + $this->attributeList = $attributeList; + $this->scopeConfig = $scopeConfig; + $this->storeManager = $storeManager; + $this->registry = $registry; + $this->translitFilter = $translitFilter; + } + + /** + * Extract filter params from URL part + * + * @param string $url + * @return array + */ + public function extract($url) + { + $byAttribute = explode(self::SEO_FILTERS_DELIMITER, $this->getFilterString($url)); + $data = []; + foreach ($byAttribute as $attributeString) { + preg_match('/[^-]*/', $attributeString, $match); + if (empty($match)) { + continue; + } + $attributeCode = $match[0]; + $attributeValues = explode( + self::SEO_FILTER_VALUES_DELIMITER, + preg_replace('/' . $attributeCode . self::SEO_FILTER_CODE_DELIMITER . '/', '', $attributeString, 1) + ); + $attribute = $this->getAttribute($attributeCode); + if (!$attribute && $attributeCode != 'cat') { + continue; + } + $options = $this->getOptions($attributeCode); + $data[$attributeCode] = []; + foreach ($attributeValues as $value) { + if ($attribute && $attribute->getBackendType() == 'decimal') { + $data[$attributeCode][] = str_replace('_', '-', $value); + continue; + } + $id = array_search($value, $options); + if ($id !== false) { + $data[$attributeCode][] = $id; + } + } + } + return $data; + } + + /** + * Hydrate filter params to url string + * + * @param array $data + * @return string + */ + public function hydrate(array $data) + { + $stringParts = []; + foreach ($data as $attributeCode => $values) { + $attributeParts = []; + $attribute = $this->getAttribute($attributeCode); + $options = $this->getOptions($attributeCode); + + foreach ($values as $value) { + if ($attribute && $attribute->getBackendType() == 'decimal') { + $attributeParts[] = str_replace('-', '_', $value); + continue; + } + $attributeParts[] = $options[$value] ?? false; + } + $stringParts[] = $attributeCode . self::SEO_FILTER_CODE_DELIMITER . implode(self::SEO_FILTER_VALUES_DELIMITER, $attributeParts); + } + return implode(self::SEO_FILTERS_DELIMITER, $stringParts); + } + + /** + * Get filter url part + * + * @param string $url + * @return string + */ + public function getFilterString($url) + { + $suffix = preg_quote($this->getSuffix(), '/'); + preg_match('/[^\/]*' . $suffix . '$/', $url, $match); + $string = ''; + if (count($match)) { + $string = $match[0]; + } + return preg_replace('/' . $suffix . '$/', '', $string); + } + + /** + * Get filterable attributes options + * + * @param null|string $attribute + * @return array + */ + protected function getOptions($attribute = null) + { + if (!$this->_optionsByCode) { + $attributeIds = $this->attributeList->getList()->getAllIds(); + + $optionsCollection = $this->_attrOptionCollectionFactory->create() + ->addFieldToFilter('main_table.attribute_id', ['in' => $attributeIds]) + ->setStoreFilter($this->storeManager->getStore()->getId()) + ; + $optionsCollection->getSelect()->joinLeft( + ['attr_table' => $optionsCollection->getTable('eav_attribute')], + 'attr_table.attribute_id = main_table.attribute_id', + ['attribute_code'] + ); + + $this->_optionsByCode = []; + foreach ($optionsCollection as $option) { + if (!isset($this->_optionsByCode[$option->getAttributeCode()])) { + $this->_optionsByCode[$option->getAttributeCode()] = []; + } + $this->_optionsByCode[$option->getAttributeCode()][$option->getOptionId()] = $this->prepareOptionLabel($option->getValue()); + } + + $currentCategoryId = $this->registry->registry('current_category_id'); + if ($currentCategoryId) { + $this->_optionsByCode['cat'] = $this->getCategoryOptions($currentCategoryId); + } + } + + if (!isset($this->_optionsByCode['cat'])) { + $currentCategory = $this->registry->registry('current_category'); + $this->_optionsByCode['cat'] = $this->getCategoryOptions($currentCategory->getId()); + } + return isset($this->_optionsByCode[$attribute]) ? $this->_optionsByCode[$attribute] : []; + } + + /** + * Get attribute model by code + * + * @param string $code + * @return bool|\Magento\Eav\Model\Entity\Attribute + */ + protected function getAttribute($code) + { + if (!$this->_attributes) { + $this->_attributes = []; + foreach ($this->attributeList->getList() as $attribute) { + $this->_attributes[$attribute->getAttributeCode()] = $attribute; + } + } + return isset($this->_attributes[$code]) ? $this->_attributes[$code] : false; + } + + /** + * Get category options + * + * @param int $parentId + * @return array + */ + protected function getCategoryOptions($parentId) + { + $options = []; + $categories = $this->categoryCollectionFactory->create()->addAttributeToSelect( + 'name' + )->addAttributeToSelect( + 'is_active' + )->addAttributeToFilter('parent_id', $parentId) + ->setStoreId( + $this->storeManager->getStore()->getId() + ); + foreach ($categories as $category) { + $options[$category->getId()] = $this->prepareOptionLabel($category->getName()); + } + return $options; + } + + /** + * Prepare option label for url + * + * @param string $label + * @return string + */ + protected function prepareOptionLabel($label) + { + return $this->translitFilter->filter($label); + } + + /** + * Get category url suffix + * + * @return string + */ + public function getSuffix() + { + return $this->scopeConfig->getValue( + \Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator::XML_PATH_CATEGORY_URL_SUFFIX, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + $this->storeManager->getStore()->getId() + ); + } +} diff --git a/Model/Url/Translit.php b/Model/Url/Translit.php new file mode 100644 index 0000000..dc5f075 --- /dev/null +++ b/Model/Url/Translit.php @@ -0,0 +1,20 @@ +getRequest()->isXmlHttpRequest()) { + $subject->getResponse()->setHeader('Content-Type', 'application/json', true); + $navigationBlock = $result->getLayout()->getBlock('catalog.leftnav'); + $productsBlock = $result->getLayout()->getBlock('category.products'); + if ($navigationBlock) { + return $subject->getResponse()->setBody(json_encode(['products' => $productsBlock->toHtml(), 'leftnav' => $navigationBlock->toHtml()])); + } + } + return $result; + } +} diff --git a/Plugin/CategoryViewBlock.php b/Plugin/CategoryViewBlock.php new file mode 100644 index 0000000..9c6927e --- /dev/null +++ b/Plugin/CategoryViewBlock.php @@ -0,0 +1,26 @@ +getNameInLayout() == 'category.products') { + $result = '
' . $result . '
'; + } + return $result; + } +} diff --git a/Plugin/FilterRenderer.php b/Plugin/FilterRenderer.php index 53c5290..b48c7b8 100644 --- a/Plugin/FilterRenderer.php +++ b/Plugin/FilterRenderer.php @@ -2,6 +2,8 @@ namespace Niks\LayeredNavigation\Plugin; use Magento\Swatches\Model\Plugin\FilterRenderer as CoreRenderer; +use Magento\Framework\App\ObjectManager; +use Magento\Store\Model\StoreManagerInterface; /** * Class FilterRenderer @@ -13,5 +15,51 @@ class FilterRenderer extends CoreRenderer * * @var string */ - protected $block = 'Niks\LayeredNavigation\Block\LayeredNavigation\RenderLayered'; + protected $block = \Niks\LayeredNavigation\Block\LayeredNavigation\RenderLayered::class; + + + /** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @param \Magento\LayeredNavigation\Block\Navigation\FilterRenderer $subject + * @param \Closure $proceed + * @param \Magento\Catalog\Model\Layer\Filter\FilterInterface $filter + * @return mixed + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function aroundRender( + \Magento\LayeredNavigation\Block\Navigation\FilterRenderer $subject, + \Closure $proceed, + \Magento\Catalog\Model\Layer\Filter\FilterInterface $filter + ) { + if ($filter->hasAttributeModel()) { + if ($this->swatchHelper->isSwatchAttribute($filter->getAttributeModel())) { + return $this->layout + ->createBlock($this->block) + ->setSwatchFilter($filter) + ->toHtml(); + } + } + + if ($this->isSliderEnabled() && $filter->hasAttributeModel() && $filter->getAttributeModel()->getBackendType() == 'decimal') { + return $this->layout + ->createBlock(\Niks\LayeredNavigation\Block\LayeredNavigation\SliderRenderer::class) + ->setFilter($filter) + ->toHtml(); + } + return $proceed($filter); + } + + protected function isSliderEnabled() + { + $scope = ObjectManager::getInstance()->get(\Magento\Framework\App\Config\ScopeConfigInterface::class); + $storeManager = ObjectManager::getInstance()->get(StoreManagerInterface::class); + + return $scope->getValue( + 'niks_layered_navigation/general/slider', + \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + $storeManager->getStore()->getId() + ); + } + + } diff --git a/Plugin/State.php b/Plugin/State.php new file mode 100644 index 0000000..ea672d6 --- /dev/null +++ b/Plugin/State.php @@ -0,0 +1,27 @@ +getActiveFilters() as $item) { + $filterState[$item->getFilter()->getRequestVar()] = $item->getFilter()->getCleanValue(); + } + $params['_navigation_filters'] = ''; + $params['_current'] = true; + $params['_use_rewrite'] = true; + $params['_escape'] = true; + $params['_query'] = $filterState; + return $subject->getUrl('*/*/*', $params); + } +} diff --git a/composer.json b/composer.json index 25d0012..3bd8e12 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,7 @@ "php": "~5.5.0|~5.6.0|~7.0.0" }, "type": "magento2-module", - "version": "0.0.4", + "version": "0.1.0", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml new file mode 100755 index 0000000..7ca347e --- /dev/null +++ b/etc/adminhtml/system.xml @@ -0,0 +1,34 @@ + + + + + + + +
+ + niks_extensions + Niks_LayeredNavigation::layered_navigation + + + + + Magento\Config\Model\Config\Source\Yesno + + + + Magento\Config\Model\Config\Source\Yesno + + + + Magento\Config\Model\Config\Source\Yesno + + +
+
+
diff --git a/etc/config.xml b/etc/config.xml new file mode 100755 index 0000000..89b3722 --- /dev/null +++ b/etc/config.xml @@ -0,0 +1,18 @@ + + + + + + + 0 + 0 + 0 + + + + diff --git a/etc/di.xml b/etc/di.xml index d40d9e4..f93c85d 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -20,21 +20,49 @@ - + - Niks\LayeredNavigation\Model\Layer\Filter\Item + Niks\LayeredNavigation\Model\Url\Builder + + + + + categoryViewContext - + - Niks\LayeredNavigation\Model\Search\Dynamic\Algorithm + categoryViewContext - + - Niks\LayeredNavigation\Model\ResourceModel\Fulltext\CollectionFactory + categoryViewContext + + + + + + categoryViewContext + + + + + categoryViewContext + + + + Niks\LayeredNavigation\Model\Url\Builder + + + + + Niks\LayeredNavigation\Model\Layer\Filter\Item + + + Niks\LayeredNavigation\Model\ResourceModel\Fulltext\Collection @@ -45,13 +73,33 @@ quick_search_container - - + + + Niks\LayeredNavigation\Model\ResourceModel\Fulltext\CollectionFactory + + + + + + + + + + + + + + + + + Niks\LayeredNavigation\Model\Search\Dynamic\Algorithm + + diff --git a/etc/frontend/di.xml b/etc/frontend/di.xml new file mode 100644 index 0000000..d2cfd6d --- /dev/null +++ b/etc/frontend/di.xml @@ -0,0 +1,14 @@ + + + + + + + Niks\LayeredNavigation\Controller\Router + false + 30 + + + + + diff --git a/etc/module.xml b/etc/module.xml index 17fe3a2..e02dd84 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -1,6 +1,6 @@ - + diff --git a/view/frontend/layout/catalog_category_view.xml b/view/frontend/layout/catalog_category_view.xml new file mode 100755 index 0000000..dc818a8 --- /dev/null +++ b/view/frontend/layout/catalog_category_view.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + diff --git a/view/frontend/requirejs-config.js b/view/frontend/requirejs-config.js new file mode 100755 index 0000000..70ebe2e --- /dev/null +++ b/view/frontend/requirejs-config.js @@ -0,0 +1,8 @@ +var config = { + map: { + '*': { + niksNavigation: 'Niks_LayeredNavigation/js/navigation', + niksSlider: 'Niks_LayeredNavigation/js/slider' + } + } +}; diff --git a/view/frontend/templates/ajax.phtml b/view/frontend/templates/ajax.phtml new file mode 100644 index 0000000..32365e4 --- /dev/null +++ b/view/frontend/templates/ajax.phtml @@ -0,0 +1,8 @@ + + diff --git a/view/frontend/templates/slider.phtml b/view/frontend/templates/slider.phtml new file mode 100644 index 0000000..b7bc14c --- /dev/null +++ b/view/frontend/templates/slider.phtml @@ -0,0 +1,21 @@ + +
+
+
+
+ - + getCurrencySymbol() ?> +
+
+
+ + diff --git a/view/frontend/web/js/navigation.js b/view/frontend/web/js/navigation.js new file mode 100644 index 0000000..de28b5c --- /dev/null +++ b/view/frontend/web/js/navigation.js @@ -0,0 +1,138 @@ +define([ + "jquery" +], function ($) { + "use strict"; + var module = { + _create: function () { + if (this.options.disabled) { + return; + } + this._initState(); + var self = this; + $('#layered-filter-block, .pages-items').off('click', 'a').on('click', 'a', function (e) { + e.preventDefault(); + var url = $(this).attr('href'); + if (url) { + self.updateContent(url, true); + } + }); + $(window).unbind('popstate').bind('popstate', this.updateContent.bind(this)); + }, + + _initState: function () { + var self = this; + if (!window.history.state) { + $(document).ready(function () { + self._saveState(document.location.href); + }); + } + + }, + + _saveState: function (url) { + window.history.pushState({url: url}, '', url); + }, + + updateContent: function (url, updateState) { + if (updateState) { + this._saveState(url); + } + if (url instanceof Object) { + url = url.originalEvent.state.url; + } + $('body').loader('show'); + var self = this; + $.ajax({ + url: url, + cache: true, + type: 'GET', + data: {niksAjax: true}, + success: function (resp) { + if (resp instanceof Object) { + $(self.options.filtersContainer).replaceWith(resp.leftnav); + $(self.options.productsContainer).replaceWith(resp.products); + $.mage.init(); + $('html, body').animate({ + scrollTop: $('#maincontent').offset().top + }, 400); + self._create(); + $('body').loader('hide'); + } + } + }); + }, + + init: function (options) { + if (!module.options) { + module.options = options; + module._create(); + } + return { + updateContent: module.updateContent.bind(module) + }; + } + }; + + return module.init; + + // $.widget('niks.navigation', { + // + // _create: function () { + // if (this.options.disabled) { + // return; + // } + // this._initState(); + // var self = this; + // $('#layered-filter-block, .pages-items').off('click', 'a').on('click', 'a', function (e) { + // e.preventDefault(); + // var url = $(this).attr('href'); + // if (url) { + // self._saveState(url); + // self._updateContent(url); + // } + // }); + // $(window).unbind('popstate').bind('popstate', this._updateContent.bind(this)); + // }, + // + // _initState: function () { + // var self = this; + // if (!window.history.state) { + // $(document).ready(function () { + // self._saveState(document.location.href); + // }); + // } + // + // }, + // + // _saveState: function (url) { + // window.history.pushState({url: url}, '', url); + // }, + // + // _updateContent: function (url) { + // if (url instanceof Object) { + // url = url.originalEvent.state.url; + // } + // $('body').loader('show'); + // var self = this; + // $.ajax({ + // url: url, + // cache: true, + // type: 'GET', + // data: {niksAjax: true}, + // success: function (resp) { + // if (resp instanceof Object) { + // $(self.options.filtersContainer).replaceWith(resp.leftnav); + // $(self.options.productsContainer).replaceWith(resp.products); + // $.mage.init(); + // $('html, body').animate({ + // scrollTop: $('#maincontent').offset().top + // }, 400); + // self._create(); + // $('body').loader('hide'); + // } + // } + // }); + // } + // }); + // return $.niks.navigation; +}); diff --git a/view/frontend/web/js/slider.js b/view/frontend/web/js/slider.js new file mode 100644 index 0000000..9f9b269 --- /dev/null +++ b/view/frontend/web/js/slider.js @@ -0,0 +1,51 @@ +define([ + "jquery", + "niksNavigation", + "jquery/ui" +], function ($, nav) { + "use strict"; + + $.widget('niks.priceSlider', { + _create: function () { + var self = this, + slider = $('#slider-' + this.options.code + '-range'), + fromInput = $('#' + self.options.code + '-from'), + toInput = $('#' + self.options.code + '-to'); + + slider.slider({ + range: true, + min: this.options.min, + max: this.options.max, + values: [this.options.from, this.options.to], + slide: function (event, ui) { + fromInput.val(ui.values[0]); + toInput.val(ui.values[1]); + }, + stop: function () { + self.processPrice(slider); + } + }); + + fromInput.val(slider.slider('values', 0)); + toInput.val(slider.slider('values', 1)); + + fromInput.change(function () { + slider.slider('values', 0, $(this).val()); + self.processPrice(slider); + }); + + toInput.change(function () { + slider.slider('values', 1, $(this).val()); + self.processPrice(slider); + }); + }, + + processPrice: function (slider) { + var from = slider.slider('values', 0), + to = slider.slider('values', 1), + url = this.options.urlTemplate.replace('{{from}}', from).replace('{{to}}', to); + nav().updateContent(url, true); + } + }); + return $.niks.priceSlider; +}); diff --git a/view/frontend/web/styles.css b/view/frontend/web/styles.css new file mode 100644 index 0000000..2b63b97 --- /dev/null +++ b/view/frontend/web/styles.css @@ -0,0 +1,61 @@ +.range-attribute-filter .ui-slider-handle { + height: 10px; + width: 10px; + -webkit-border-radius: 10px; + border-radius: 10px; + background: #003eff; + display: block; + padding: 0px; + margin-left: 0px; +} + +.range-attribute-filter .ui-slider-handle:nth-of-type(2) { + margin-left: -10px; +} + +.range-attribute-filter .ui-state-active, +.range-attribute-filter .ui-widget-content .ui-state-active, +.range-attribute-filter .ui-widget-header .ui-state-active, +.range-attribute-filter a.ui-button:active, +.range-attribute-filter .ui-button:active, +.range-attribute-filter .ui-button.ui-state-active:hover { + background: #114ec3; + color: #114ec3; +} + +.range-attribute-filter .ui-icon-background, +.range-attribute-filter .ui-state-active .ui-icon-background { + border: #114ec3; + background-color: #114ec3; +} + +.range-attribute-filter .ui-state-active a, +.range-attribute-filter .ui-state-active a:link, +.range-attribute-filter .ui-state-active a:visited { + color: #114ec3; + text-decoration: none; +} + +.range-attribute-filter .ui-state-hover, +.range-attribute-filter .ui-widget-content .ui-state-hover, +.range-attribute-filter .ui-widget-header .ui-state-hover, +.range-attribute-filter .ui-button:hover { + + background: #114ec3; + font-weight: normal; + color: #114ec3; +} +.range-attribute-filter .range { + padding-top:10px; + text-align: center; +} +.range-attribute-filter .range input { + max-width: 35px; + border-radius: 5px; +} + +.range-attribute-filter .range .range-wrapper{ + padding-left: 10px; + float: left; + max-width: 45%; +}