Mobile Facet using Desktop Filters

642

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

636
//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;
}