Mobile Facet using Desktop Filters
There are 2 options to utilize desktop filters within the mobile filter menu:
Option 1 - Default Logic (when only one filter can be expanded)
Option 2 - allows to expand multiple filters at the same time
Option 1
/* VARIANT 1 */
import DesktopFacets from 'components/search/DesktopFacets';
{/*... some code ...*/}
return (
{/* Place DesktopFacets component and remove Branch */}
<DesktopFacets />
{/*
<Branch
config={config}
theme={theme}
selectFacet={selectFacet}
active={facets.find((f) => f.get('name') === activeFacetName)}
facets={facets}
condition={activeFacet}
right={FacetTitles}
left={FacetContent}
/>
*/}
{/* ...rest */}
/* styles for MobileFacets
don't forget to put .findify-components-search--mobile-facets__modal.mobile before styling DesktopFacets in MobileFacets
*/
.findify-components-search--mobile-facets__modal.mobile .findify-components-search--desktop-facets__horizontal .findify-components-search--desktop-facets__container {
flex-direction: column;
}
.findify-components-search--mobile-facets__modal.mobile .findify-components--facet__horizontal {
margin: 0;
}
.findify-components-search--mobile-facets__modal.mobile .findify-components--facet__horizontal .findify-components--facet__body {
position: relative;
border: none;
}
.findify-components-search--mobile-facets__modal.mobile .findify-components--facet__horizontal .findify-components--facet__title {
background-color: #fff;
}
.findify-components-search--mobile-facets__modal.mobile .findify-components--facet__horizontal .findify-components--facet__body {
background-color: #fff;
}
.findify-components-search--mobile-facets__modal.mobile .findify-components--facet__horizontal.findify-components--facet__expanded .findify-components--facet__title:after {
background-color: #fff;
}
Option 2
import DesktopFacets from 'components/search/DesktopFacets';
{/* ...some code... */}
/* Check For Mobile Version */
const isMobile = window.innerWidth < config.toJS().mobileBreakpoint;
return (
{/* some code */}
{/* Place DesktopFacets component and remove Branch, pass down props */}
<DesktopFacets isMobile={isMobile} />
{/*
<Branch
config={config}
theme={theme}
selectFacet={selectFacet}
active={facets.find((f) => f.get('name') === activeFacetName)}
facets={facets}
condition={activeFacet}
right={FacetTitles}
left={FacetContent}
/>
*/}
{/* ...rest */}
/* accept isMobile props */
export default memo(({ theme = styles, isMobile }) => {
{/* ...some code ... */}
{/* const toggleFacet already exists, you just need to change if statement */}
const toggleFacet = (name) =>
setFacetsStates((facets) => {
const isOpen = facets.includes(name);
{/* check for isMobile */
if(isMobile || !isAccordion) return isOpen ? [...facets.filter((k) => k !== name)] : [...facets, name];
if (isAccordion) return isOpen ? [] : [name];
});
{/* ..rest */}
/* styles for MobileFacets
don't forget to put .findify-components-search--mobile-facets__modal.mobile before styling DesktopFacets in MobileFacets
*/
.findify-components-search--mobile-facets__modal.mobile .findify-components-search--desktop-facets__horizontal .findify-components-search--desktop-facets__container {
flex-direction: column;
}
.findify-components-search--mobile-facets__modal.mobile .findify-components--facet__horizontal {
margin: 0;
}
.findify-components-search--mobile-facets__modal.mobile .findify-components--facet__horizontal .findify-components--facet__body {
position: relative;
border: none;
}
.findify-components-search--mobile-facets__modal.mobile .findify-components--facet__horizontal .findify-components--facet__title {
background-color: #fff;
}
.findify-components-search--mobile-facets__modal.mobile .findify-components--facet__horizontal .findify-components--facet__body {
background-color: #fff;
}
.findify-components-search--mobile-facets__modal.mobile .findify-components--facet__horizontal.findify-components--facet__expanded .findify-components--facet__title:after {
background-color: #fff;
}
Adding Breadcrumbs
//import Breadcrubms component
import Breadcrumbs from 'components/Breadcrumbs';
/place it wherever you need
<Breadcrumbs />
Complete Example
Option 1 - Default Logic (when only one filter can be expanded)
Option 2 - allows to expand multiple filters at the same time
Option 1
/**
* @module components/search/MobileFacets
*/
/*
import Branch from 'components/common/Branch';
import FacetTitles from 'components/search/MobileFacets/Titles';
import Component from 'components/Facet/Component';
*/
import Button from 'components/Button';
import cx from 'classnames';
import Icon from 'components/Icon';
import Text from 'components/Text';
import { ThemedSFCProps, IFacet, MJSConfiguration, MJSValue } from 'types';
import { List } from 'immutable';
import styles from 'components/search/MobileFacets/styles.css';
import { useFacets, useQuery } from '@findify/react-connect';
import { memo, useCallback, useMemo, useState } from 'react';
import { Immutable } from '@findify/store-configuration';
import useTranslations from 'helpers/useTranslations';
import { Facet } from '@findify/react-connect/types/immutable/facets';
//import DesktopFacets and Breadcrumbs
import DesktopFacets from 'components/search/DesktopFacets';
import Breadcrumbs from 'components/Breadcrumbs';
/** Props that FacetContent accepts */
export interface IFacetContentProps extends ThemedSFCProps {
/** Currently active facet */
active: Facet;
/** MJS Configuration */
config: Immutable.SearchConfig;
}
{/*
const FacetContent = ({
active,
config,
theme = styles,
}: IFacetContentProps) => {
const _config = config.merge(
config.getIn(['facets', 'filters', active.get('name')])
);
return (
<div className={cx(theme.container, theme[_config.get('type')])}>
<Component
isExpanded
isMobile
type={_config.get('type')}
facet={active}
config={_config}
/>
</div>
);
};
*/}
/** Props that MobileFacets view accepts */
export interface IMobileFacetsProps extends ThemedSFCProps {
/** immutable.List() of Facets */
facets: List<IFacet>;
/** Currently active facet */
activeFacet?: IFacet;
/** Method used to select a facet */
selectFacet: (name?: string) => any;
/** Method used to reset facet */
onReset: () => any;
/** MJS API Request Metadata */
meta: Map<string, MJSValue>;
/** Method used for hiding modal / drawer */
hideModal: (name: string) => any;
/** Total filters selected */
total: number;
/** Filters selected for active facet */
filtersSelected: number;
}
export default memo(({ theme = styles, hideModal }: IMobileFacetsProps) => {
const { facets, config, update } = useFacets<Immutable.SearchConfig>();
const { query } = useQuery();
const translate = useTranslations();
const [activeFacetName, setActiveFacet] = useState<string | null>(null);
const total = useMemo(
() =>
query
.get('filters')
?.reduce(
(acc, filter) =>
acc +
(/category[2-9]/.test(filter.get('name'))
? 0
: filter.get('values').size),
0
) || 0,
[query]
);
const activeFacet = useMemo(
() => facets.find((f) => f.get('name') === activeFacetName),
[activeFacetName]
);
const filtersSelected = useMemo(() => {
if (!activeFacet) return 0;
return activeFacet.get('values').filter((item) => item.get('selected'))
.size;
}, [activeFacet]);
const selectFacet = useCallback((name) => {
setActiveFacet(name || null);
}, []);
const onReset = useCallback(() => {
update('filters', (f) => f?.clear());
}, []);
return (
<div className={cx(theme.modal, 'mobile')}>
<div className={theme.header}>
<div className={theme.title}>
<Text primary uppercase display-if={!activeFacet}>
{translate('facets.filters')}
</Text>
<Text
secondary
uppercase
display-if={!activeFacet && total}
className={theme.filterCount}
>
({total})
</Text>
<Text primary uppercase display-if={activeFacet}>
{config
.getIn(['facets', 'filters', activeFacet?.get('name')])
?.get('label')}
</Text>
<Text
secondary
uppercase
display-if={activeFacet && filtersSelected}
className={theme.filterCount}
>
({filtersSelected})
</Text>
</div>
<Button
onClick={activeFacet ? selectFacet : hideModal}
className={theme.backButton}
>
<Icon name="ArrowBack" title={translate('facets.back')} />
</Button>
<Button display-if={query?.get('filters')?.size} onClick={onReset}>
<Text secondary uppercase>
{translate('facets.clearAll')}
</Text>
</Button>
</div>
<div className={theme.body}>
{/* Place DesktopFacets component and remove Branch */}
<Breadcrumbs />
<DesktopFacets />
{/*
<Branch
config={config}
theme={theme}
selectFacet={selectFacet}
active={facets.find((f) => f.get('name') === activeFacetName)}
facets={facets}
condition={activeFacet}
right={FacetTitles}
left={FacetContent}
/>
*/}
</div>
<Button
className={theme.footer}
onClick={activeFacet ? selectFacet : hideModal}
>
{activeFacet
? translate('facets.done')
: translate('facets.seeResults')}
</Button>
</div>
);
});
/* styles for MobileFacets
don't forget to put .findify-components-search--mobile-facets__modal.mobile before styling DesktopFacets in MobileFacets
*/
.findify-components-search--mobile-facets__modal.mobile .findify-components-search--desktop-facets__horizontal .findify-components-search--desktop-facets__container {
flex-direction: column;
}
.findify-components-search--mobile-facets__modal.mobile .findify-components--facet__horizontal {
margin: 0;
}
.findify-components-search--mobile-facets__modal.mobile .findify-components--facet__horizontal .findify-components--facet__body {
position: relative;
border: none;
}
.findify-components-search--mobile-facets__modal.mobile .findify-components--facet__horizontal .findify-components--facet__title {
background-color: #fff;
}
.findify-components-search--mobile-facets__modal.mobile .findify-components--facet__horizontal .findify-components--facet__body {
background-color: #fff;
}
.findify-components-search--mobile-facets__modal.mobile .findify-components--facet__horizontal.findify-components--facet__expanded .findify-components--facet__title:after {
background-color: #fff;
}
Option 2
/**
* @module components/search/MobileFacets
*/
/*
import Branch from 'components/common/Branch';
import FacetTitles from 'components/search/MobileFacets/Titles';
import Component from 'components/Facet/Component';
*/
import Button from 'components/Button';
import cx from 'classnames';
import Icon from 'components/Icon';
import Text from 'components/Text';
import { ThemedSFCProps, IFacet, MJSConfiguration, MJSValue } from 'types';
import { List } from 'immutable';
import styles from 'components/search/MobileFacets/styles.css';
import { useFacets, useQuery } from '@findify/react-connect';
import { memo, useCallback, useMemo, useState } from 'react';
import { Immutable } from '@findify/store-configuration';
import useTranslations from 'helpers/useTranslations';
import { Facet } from '@findify/react-connect/types/immutable/facets';
//import DesktopFacets and Breadcrumbs
import DesktopFacets from 'components/search/DesktopFacets';
import Breadcrumbs from 'components/Breadcrumbs';
/** Props that FacetContent accepts */
export interface IFacetContentProps extends ThemedSFCProps {
/** Currently active facet */
active: Facet;
/** MJS Configuration */
config: Immutable.SearchConfig;
}
{/*
const FacetContent = ({
active,
config,
theme = styles,
}: IFacetContentProps) => {
const _config = config.merge(
config.getIn(['facets', 'filters', active.get('name')])
);
return (
<div className={cx(theme.container, theme[_config.get('type')])}>
<Component
isExpanded
isMobile
type={_config.get('type')}
facet={active}
config={_config}
/>
</div>
);
};
*/}
/** Props that MobileFacets view accepts */
export interface IMobileFacetsProps extends ThemedSFCProps {
/** immutable.List() of Facets */
facets: List<IFacet>;
/** Currently active facet */
activeFacet?: IFacet;
/** Method used to select a facet */
selectFacet: (name?: string) => any;
/** Method used to reset facet */
onReset: () => any;
/** MJS API Request Metadata */
meta: Map<string, MJSValue>;
/** Method used for hiding modal / drawer */
hideModal: (name: string) => any;
/** Total filters selected */
total: number;
/** Filters selected for active facet */
filtersSelected: number;
}
export default memo(({ theme = styles, hideModal }: IMobileFacetsProps) => {
const { facets, config, update } = useFacets<Immutable.SearchConfig>();
const { query } = useQuery();
const translate = useTranslations();
const [activeFacetName, setActiveFacet] = useState<string | null>(null);
const total = useMemo(
() =>
query
.get('filters')
?.reduce(
(acc, filter) =>
acc +
(/category[2-9]/.test(filter.get('name'))
? 0
: filter.get('values').size),
0
) || 0,
[query]
);
const activeFacet = useMemo(
() => facets.find((f) => f.get('name') === activeFacetName),
[activeFacetName]
);
const filtersSelected = useMemo(() => {
if (!activeFacet) return 0;
return activeFacet.get('values').filter((item) => item.get('selected'))
.size;
}, [activeFacet]);
const selectFacet = useCallback((name) => {
setActiveFacet(name || null);
}, []);
const onReset = useCallback(() => {
update('filters', (f) => f?.clear());
}, []);
/* Check For Mobile Version */
const isMobile = window.innerWidth < config.toJS().mobileBreakpoint;
return (
<div className={cx(theme.modal, 'mobile')}>
<div className={theme.header}>
<div className={theme.title}>
<Text primary uppercase display-if={!activeFacet}>
{translate('facets.filters')}
</Text>
<Text
secondary
uppercase
display-if={!activeFacet && total}
className={theme.filterCount}
>
({total})
</Text>
<Text primary uppercase display-if={activeFacet}>
{config
.getIn(['facets', 'filters', activeFacet?.get('name')])
?.get('label')}
</Text>
<Text
secondary
uppercase
display-if={activeFacet && filtersSelected}
className={theme.filterCount}
>
({filtersSelected})
</Text>
</div>
<Button
onClick={activeFacet ? selectFacet : hideModal}
className={theme.backButton}
>
<Icon name="ArrowBack" title={translate('facets.back')} />
</Button>
<Button display-if={query?.get('filters')?.size} onClick={onReset}>
<Text secondary uppercase>
{translate('facets.clearAll')}
</Text>
</Button>
</div>
<div className={theme.body}>
{/* Place DesktopFacets component and remove Branch, pass down props */}
<Breadcrumbs />
<DesktopFacets isMobile={isMobile} />
{/*
<Branch
config={config}
theme={theme}
selectFacet={selectFacet}
active={facets.find((f) => f.get('name') === activeFacetName)}
facets={facets}
condition={activeFacet}
right={FacetTitles}
left={FacetContent}
/>
*/}
</div>
<Button
className={theme.footer}
onClick={activeFacet ? selectFacet : hideModal}
>
{activeFacet
? translate('facets.done')
: translate('facets.seeResults')}
</Button>
</div>
);
});
/**
* @module components/search/DesktopFacets
*/
import Branch from 'components/common/Branch';
import MapArray from 'components/common/MapArray';
import Facet from 'components/Facet';
import Sticky from 'components/common/Sticky';
import Title from 'components/search/DesktopFacets/Title';
import { useFacets } from '@findify/react-connect';
import { Immutable } from '@findify/store-configuration';
import { memo, useCallback, useMemo, useState } from 'react';
import useTranslations from 'helpers/useTranslations';
import { useEvents, emit } from 'helpers/emmiter';
import styles from 'components/search/DesktopFacets/styles.css';
const DefaultContent = ({ theme, children, title }) => (
<section className={theme.root} role="region" aria-label={title} tabIndex={0}>
<div className={theme.container}>{children}</div>
</section>
);
/* accept isMobile props */
export default memo(({ theme = styles, isMobile }) => {
const { facets, meta, onReset, config } = useFacets<Immutable.SearchConfig>();
const translate = useTranslations();
const [isHorizontal, isHidable, isSticky, isAccordion] = useMemo(
() => [
config.getIn(['facets', 'position']) === 'top',
config.getIn(['facets', 'hidable']),
config.getIn(['facets', 'sticky']),
config.getIn(['facets', 'accordion']),
],
[]
);
const [visible, setVisible] = useState(true);
/** Generate initial list of opened facets */
const [openFacets, setFacetsStates] = useState(
config
.getIn(['facets', 'filters'])
.filter((f) => !f.get('initiallyCollapsed'))
.keySeq()
.toArray()
);
const toggleFacet = (name) =>
setFacetsStates((facets) => {
const isOpen = facets.includes(name);
if(isMobile || !isAccordion) return isOpen ? [...facets.filter((k) => k !== name)] : [...facets, name];
if (isAccordion) return isOpen ? [] : [name];
});
useEvents({
showFacets: () => setVisible(true),
toggleFacet: (name) => toggleFacet(name),
});
const hideFacets = useCallback(() => {
emit('hideFacets');
setVisible(false);
}, []);
return (
<Branch
display-if={!isHidable || visible}
theme={{
container: theme.container,
root: isHorizontal ? theme.horizontal : theme.root,
}}
condition={isSticky}
title={translate('facets.filters')}
left={Sticky}
right={DefaultContent}
stickToTop={isHorizontal}
offset={isHorizontal ? 0 : 25}
>
<Title
meta={meta}
theme={theme}
onReset={onReset}
onHide={hideFacets}
hidable={isHidable}
/>
<MapArray
array={facets}
factory={Facet}
config={config}
isHorizontal={isHorizontal}
onToggle={toggleFacet}
openFacets={openFacets}
keyAccessor={(i) => i.get('name')}
/>
</Branch>
);
});
/* styles for MobileFacets
don't forget to put .findify-components-search--mobile-facets__modal.mobile before styling DesktopFacets in MobileFacets
*/
.findify-components-search--mobile-facets__modal.mobile .findify-components-search--desktop-facets__horizontal .findify-components-search--desktop-facets__container {
flex-direction: column;
}
.findify-components-search--mobile-facets__modal.mobile .findify-components--facet__horizontal {
margin: 0;
}
.findify-components-search--mobile-facets__modal.mobile .findify-components--facet__horizontal .findify-components--facet__body {
position: relative;
border: none;
}
.findify-components-search--mobile-facets__modal.mobile .findify-components--facet__horizontal .findify-components--facet__title {
background-color: #fff;
}
.findify-components-search--mobile-facets__modal.mobile .findify-components--facet__horizontal .findify-components--facet__body {
background-color: #fff;
}
.findify-components-search--mobile-facets__modal.mobile .findify-components--facet__horizontal.findify-components--facet__expanded .findify-components--facet__title:after {
background-color: #fff;
}
Updated about 1 year ago