AppmakerDeveloper

Customising Filter and Sort Modal

This doc is for showing how to customize sort and filter modal

  1. Register the block in the blocks/index.js
import CollectionSortFilter from './components/CollectionSortFilter/index.js';

const blocks = [
  // other blocks
  {
    name: 'shopify/collection-filter',
    View: CollectionSortFilter,
  },
];

export { blocks };
  1. Inside CollectionSortFilter/index.js, add this code
import React from 'react';
import { View, StyleSheet } from 'react-native';
import { ShopifyFilterProviderWrapper } from '@appmaker-xyz/shopify';
import FilterModal from './filter/FilterModal';
import FilterButton from './filter/components/FilterButton';
import SortButton from '../sort/components/SortButton';
import SortModal from '../sort/SortModal';
export default function Filter(props) {
  const { attributes } = props;
  const { filterButtonStyle, sortButtonStyle } = attributes;
  return (
    <ShopifyFilterProviderWrapper {...props}>
      <View style={styles.container}>
        <FilterButton
          attributes={{
            customStyles: filterButtonStyle,
          }}
        />
        <View style={styles.separator} />
        <SortButton
          attributes={{
            customStyles: sortButtonStyle,
          }}
        />
      </View>
      <SortModal />
      <FilterModal />
    </ShopifyFilterProviderWrapper>
  );
}
  1. Inside filter/FilterModal.js, add this code
import {
  View,
  Text,
  StyleSheet,
  Pressable,
  FlatList,
  SafeAreaView,
  ActivityIndicator,
} from 'react-native';
import React from 'react';
import Modal from 'react-native-modal';
import Icon from '@appmaker-xyz/uikit/Icons/Feather';
import {
  useFilterOptions,
  useCollectionFilter,
  useFilterModal,
  useSelectedFiltersCount,
  useIsAnyFilterSelected,
} from '@appmaker-xyz/shopify';
import PriceRange from './components/PriceRange';
import FilterListOption from './components/FilterListOption';
import FilterColorSwatch from './components/FilterColorSwatch';
import { AppmakerText } from '@appmaker-xyz/uikit';
import { appmaker } from '@appmaker-xyz/core';

const FilterOptions = {
  LIST: FilterListOption,
  PRICE_RANGE: PriceRange,
  COLOR_SWATCH: FilterColorSwatch,
  HIDDEN: () => null,
};

function FilterItemContent({ item }) {
  const { selectFilter, removeFilter } = useFilterOptions();
  const { label, type } = item;
  const FilterComponent = FilterOptions[type];
  return (
    <View style={styles.itemContainer}>
      {FilterComponent ? (
        <FilterComponent
          item={item}
          selectFilter={selectFilter}
          removeFilter={removeFilter}
          filterKey={item.id}
        />
      ) : null}
    </View>
  );
}

function ModalHeader(props) {
  const { clearSelectedFilters } = useFilterOptions();
  const isFilterSelected = useIsAnyFilterSelected();

  return (
    <View style={styles.topBar}>
      <Pressable style={styles.back} onPress={props.onClose}>
        <Icon name="x" size={20} color="#000" />
      </Pressable>
      <View style={styles.filtersHeadTextContainer}>
        <AppmakerText style={styles.filtersHeadText}>Filters</AppmakerText>
        {props.loading ? <ActivityIndicator size={16} color="#1B1B1B" /> : null}
      </View>
      {isFilterSelected ? (
        <Pressable
          onPress={clearSelectedFilters}
          style={styles.removeAllButton}>
          <AppmakerText style={styles.clearAllText}>Clear All</AppmakerText>
        </Pressable>
      ) : null}
    </View>
  );
}

function FilterItem({ item, selectTab, isCurrentTab }) {
  const filtersCount = useSelectedFiltersCount(item.id);
  const currentTabStyle = isCurrentTab ? styles.currentTab : {};
  const label = item?.upper_case ? item.label.toUpperCase() : item.label;
  const isDisabled = item?.empty === true;
  let textColor = {
    color: 'black',
  };
  if (isDisabled) {
    textColor = {
      color: '#A1A1AA',
    };
  }
  return (
    <Pressable onPress={() => selectTab(item.id)}>
      <View style={[styles.tabBarStyle, currentTabStyle]}>
        <Text style={{ ...styles.tabText, ...textColor }}>{label}</Text>
        {filtersCount > 0 ? (
          <Text style={styles.activeCountText}>{filtersCount}</Text>
        ) : null}
      </View>
    </Pressable>
  );
}

function FilterContent({ avilableFilters }) {
  const [currentTabId, selectTabId] = React.useState(avilableFilters[0]?.id);
  const tabIndexMap = React.useMemo(() => {
    const map = {};
    avilableFilters.forEach((item, index) => {
      map[item.id] = index;
    });
    return map;
  }, [avilableFilters]);
  function renderItem({ item }) {
    return (
      <FilterItem
        item={item}
        selectTab={selectTabId}
        isCurrentTab={item?.id === currentTabId}
      />
    );
  }
  const activeFilters = avilableFilters[tabIndexMap[currentTabId]];
  return (
    <View style={styles.filterView}>
      <View style={styles.filterListingView}>
        <FlatList
          data={avilableFilters}
          renderItem={renderItem}
          contentContainerStyle={styles.containerStyle}
          keyExtractor={(item, index) => index.toString()}
        />
      </View>
      <View style={styles.filterContentView}>
        {activeFilters ? <FilterItemContent item={activeFilters} /> : null}
      </View>
    </View>
  );
}

export default function FilterModal() {
  const { avilableFilters, applyFilters, isNextFiltersLoading } =
    useCollectionFilter();
  const { isFilterModalShown, closeFilterModal } = useFilterModal();
  return (
    <View style={styles.mainContainer}>
      <Modal
        isVisible={isFilterModalShown}
        style={styles.modal}
        onDismiss={closeFilterModal}
        onRequestClose={closeFilterModal}>
        <SafeAreaView style={styles.modalContainer}>
          <ModalHeader
            onClose={closeFilterModal}
            loading={isNextFiltersLoading}
          />
          {avilableFilters?.length > 0 ? (
            <>
              <FilterContent avilableFilters={avilableFilters} />
              <View style={styles.applyButtonContainer}>
                <Pressable
                  onPress={() => {
                    applyFilters();
                    closeFilterModal();
                  }}
                  style={styles.applyButton}>
                  <AppmakerText style={styles.applyButtonText}>
                    Apply Filters
                  </AppmakerText>
                </Pressable>
              </View>
            </>
          ) : null}
        </SafeAreaView>
      </Modal>
    </View>
  );
}

const styles = StyleSheet.create({
  mainContainer: {
    // flex: 1,
    // backgroundColor: 'red',
    margin: 0,
    padding: 0,
  },
  modal: {
    margin: 0,
    backgroundColor: '#f2f2f2',
  },
  modalContainer: {
    backgroundColor: '#fff',
    flex: 1,
    margin: 0,
    padding: 0,
    width: '100%',
  },
  topBar: {
    flexDirection: 'row',
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'space-between',
    paddingHorizontal: 12,
    paddingVertical: 12,
    borderBottomColor: '#e2e2e2',
    borderBottomWidth: 0.5,
  },
  back: {
    padding: 6,
    backgroundColor: '#f2f2f2',
    borderRadius: 18,
    marginRight: 12,
  },
  removeAllButton: {
    paddingVertical: 6,
  },
  clearAllText: {
    color: '#FF0000',
  },
  filtersHeadTextContainer: {
    flexDirection: 'row',
    alignItems: 'center',
    flexGrow: 1,
  },
  filtersHeadText: {
    fontFamily: 'NunitoSans-Bold',
    color: '#1B1B1B',
    marginRight: 12,
  },
  tabBarStyle: {
    flexGrow: 1,
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    paddingVertical: 8,
    paddingHorizontal: 10,
    marginVertical: 1,
  },
  currentTab: {
    backgroundColor: '#fff',
  },
  tabText: { flexShrink: 1 },
  activeCountText: {
    color: '#64748B',
    padding: 2,
    borderRadius: 4,
    marginLeft: 10,
    fontSize: 12,
  },
  filterView: {
    flex: 1,
    backgroundColor: 'white',
    flexDirection: 'row',
  },
  filterListingView: {
    flex: 2,
    backgroundColor: '#F8F6F2',
  },
  filterContentView: {
    flex: 3,
  },
  applyButtonContainer: {
    padding: 10,
    backgroundColor: '#fff',
    borderTopColor: '#E9EDF1',
    borderTopWidth: 1,
  },
  applyButton: {
    backgroundColor: '#000',
    paddingVertical: 12,
    borderRadius: 8,
  },
  applyButtonText: {
    color: '#fff',
    fontSize: 16,
    textAlign: 'center',
  },
});
  1. Inside filter/components/FilterButton.js, add this code
import React from 'react';
import { Pressable, StyleSheet, Text, View } from 'react-native';
import Icon from 'react-native-vector-icons/Feather';
import { useFilterModal, useIsAnyFilterSelected } from '@appmaker-xyz/shopify';
import { AppmakerText } from '@appmaker-xyz/uikit';
import FilterIcon from '../assets/FilterIcon';

export function FilterButton({ attributes: { customStyles = {} } = {} }) {
  const {
    activeColor,
    bgColor,
    textColor,
    outline,
    noIcon,
    small,
    wholeContainerStyle,
  } = customStyles;

  const { openFilterModal } = useFilterModal();
  const isFilterSelected = useIsAnyFilterSelected();

  const containerStyles = [styles.filterButton];
  const activeContainerStyles = [styles.activeContainer];

  if (bgColor) {
    containerStyles.push({ backgroundColor: bgColor });
  }
  if (outline) {
    containerStyles.push({ borderWidth: 1, borderColor: outline });
  }
  if (noIcon) {
    activeContainerStyles.push({ marginLeft: 0 });
  }
  if (small) {
    containerStyles.push({ paddingVertical: 8 });
  }
  if (wholeContainerStyle) {
    containerStyles.push(wholeContainerStyle);
  }

  return (
    <Pressable onPress={openFilterModal} style={containerStyles}>
      {noIcon ? null : <FilterIcon color={textColor} />}
      <View style={activeContainerStyles}>
        {isFilterSelected ? (
          <View
            style={[
              styles.activeDot,
              { backgroundColor: activeColor || '#ff0000' },
            ]}
          />
        ) : null}
        <AppmakerText
          style={styles.filterButtonText}
          fontColor={textColor ? textColor : '#fff'}>
          Filter
        </AppmakerText>
      </View>
    </Pressable>
  );
}

const styles = StyleSheet.create({
  filterButton: {
    backgroundColor: '#000',
    paddingVertical: 12,
    flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'center',
    flex: 1,
  },
  filterButtonText: {
    textAlign: 'center',
    fontSize: 16,
  },
  activeContainer: {
    position: 'relative',
    marginLeft: 6,
  },
  activeDot: {
    position: 'absolute',
    zIndex: 1,
    top: 2,
    right: -16,
    width: 8,
    height: 8,
    borderRadius: 4,
    marginRight: 6,
  },
});

export default FilterButton;
  1. Inside filter/components/FilterColorSwatch.js, add this code
import React from 'react';
import { CheckBox } from '@appmaker-xyz/ui';
import { useSelectedFilterItem } from '@appmaker-xyz/shopify';
import { FlatList, StyleSheet, Text, Image, View } from 'react-native';

function ItemLabel({ item, count, isUpperCase, label }) {
  if (item?.displayType === 'IMAGE' && item?.image) {
    return (
      <View style={styles.imageContainer}>
        <Image
          source={{ uri: item.image }}
          style={styles.colorBox}
          resizeMode="cover"
        />
        <Text style={styles.activeCountText}>({count})</Text>
      </View>
    );
  } else if (item?.displayType === 'COLOR_CODE' && item?.color_code) {
    return (
      <View style={styles.imageContainer}>
        <View style={[styles.colorBox, { backgroundColor: item.color_code }]} />
        <Text style={styles.activeCountText}>({count})</Text>
      </View>
    );
  } else {
    return (
      <View style={styles.imageContainer}>
        <Text
          style={
            isUpperCase ? styles.checkBoxItemCaptialize : styles.checkBoxItem
          }>
          {label}
        </Text>
        <Text style={styles.activeCountText}>({count})</Text>
      </View>
    );
  }
}

function CheckBoxItem({
  label,
  count,
  selectFilter,
  removeFilter,
  filterKey,
  item,
  parentItem,
}) {
  const selectedItem = useSelectedFilterItem({
    filterKey,
    filterValueId: item.id,
  });
  const isUpperCase = parentItem?.upper_case || false;

  return (
    <CheckBox
      activeColor={'#000'}
      small={true}
      label={
        <ItemLabel
          item={item}
          count={count}
          isUpperCase={isUpperCase}
          label={label}
        />
      }
      value={selectedItem?.id === item.id}
      onValueChange={(status) => {
        if (status) {
          selectFilter(filterKey, item.id, item);
        } else {
          removeFilter(filterKey, item.id, item);
        }
      }}
    />
  );
}

function FilterColorSwatch({ item, selectFilter, removeFilter }) {
  const { values } = item;

  function renderItem({ item: filterItem }) {
    return (
      <CheckBoxItem
        label={filterItem.label}
        count={filterItem.count}
        selectFilter={selectFilter}
        removeFilter={removeFilter}
        filterKey={item.id}
        item={filterItem}
        parentItem={item}
      />
    );
  }
  return (
    <FlatList
      data={values}
      renderItem={renderItem}
      contentContainerStyle={styles.filterContentViewContainer}
      keyExtractor={(item, index) => index.toString()}
    />
  );
}

const styles = StyleSheet.create({
  activeCountText: {
    color: '#64748B',
    padding: 2,
    borderRadius: 4,
    marginLeft: 6,
    fontSize: 12,
  },
  filterContentViewContainer: {
    padding: 10,
  },
  checkBoxItemCaptialize: {
    textTransform: 'capitalize',
  },
  checkBoxItem: {},
  imageContainer: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  colorBox: {
    width: 50,
    height: 22,
    borderRadius: 25,
    overflow: 'hidden',
  },
});

export default FilterColorSwatch;
  1. Inside filter/components/FilterListOption.js, add this code
import React from 'react';
import { CheckBox } from '@appmaker-xyz/ui';
import { useSelectedFilterItem } from '@appmaker-xyz/shopify';
import { FlatList, StyleSheet, Text } from 'react-native';

function CheckBoxItem({
  label,
  count,
  selectFilter,
  removeFilter,
  filterKey,
  item,
  parentItem,
}) {
  const isEmpty = item?.empty === true;
  const selectedItem = useSelectedFilterItem({
    filterKey,
    filterValueId: item.id,
  });
  const isUpperCase = parentItem?.upper_case || false;
  let colorStyle = {
    color: 'black',
  };
  if (isEmpty) {
    colorStyle = {
      color: '#A1A1AA',
    };
  }
  return (
    <CheckBox
      activeColor={'#000'}
      small={true}
      disable={isEmpty}
      label={
        <>
          <Text
            style={
              (isUpperCase
                ? styles.checkBoxItemCaptialize
                : styles.checkBoxItem,
              colorStyle)
            }>
            {label}
          </Text>
          <Text style={styles.activeCountText}>({count})</Text>
        </>
      }
      value={selectedItem?.id === item.id}
      onValueChange={(status) => {
        if (status) {
          selectFilter(filterKey, item.id, item);
        } else {
          removeFilter(filterKey, item.id, item);
        }
      }}
    />
  );
}

function FilterListOption({ item, selectFilter, removeFilter }) {
  const { values } = item;

  function renderItem({ item: filterItem }) {
    return (
      <CheckBoxItem
        label={filterItem.label}
        count={filterItem.count}
        selectFilter={selectFilter}
        removeFilter={removeFilter}
        filterKey={item.id}
        item={filterItem}
        parentItem={item}
      />
    );
  }
  return (
    <FlatList
      data={values}
      renderItem={renderItem}
      contentContainerStyle={styles.filterContentViewContainer}
      keyExtractor={(item, index) => index.toString()}
    />
  );
}

const styles = StyleSheet.create({
  activeCountText: {
    color: '#64748B',
    padding: 2,
    borderRadius: 4,
    marginLeft: 10,
    fontSize: 12,
  },
  filterContentViewContainer: {
    padding: 10,
  },
  checkBoxItemCaptialize: {
    textTransform: 'capitalize',
  },
  checkBoxItem: {},
});

export default FilterListOption;
  1. Inside filter/components/PriceRange.js, add this code
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import MultiSlider from '@ptomasroos/react-native-multi-slider';
import { styles as stylesConfig } from '@appmaker-xyz/uikit';
import { useSelectedFilterItem } from '@appmaker-xyz/shopify';
const { spacing, color } = stylesConfig;

function getMinMax(item) {
  let min = 0;
  let max = 10000;
  try {
    const config = JSON.parse(item?.values[0].input);
    min = config.price.min;
    max = config.price.max;
  } catch (error) {}
  return {
    min,
    max,
    availableMin: item?.values?.[0]?.availableMin,
    availableMax: item?.values?.[0]?.availableMax,
  };
}

function CustomLabel(props) {
  return (
    <View style={styles.customLabelContainer}>
      <Text style={styles.customLabelText}>{props.oneMarkerValue}</Text>
      <Text style={styles.customLabelText}>{props.twoMarkerValue}</Text>
    </View>
  );
}

function PriceRange({ item, filterKey, selectFilter }) {
  const filterValueId = 'price_range';
  const selectedItem = useSelectedFilterItem({
    filterKey,
    filterValueId: filterKey,
  });
  const { min, max, availableMin, availableMax } = getMinMax(item);
  if (min === max || min > max) {
    return null;
  }
  return (
    <View style={styles.sliderContainer}>
      {/* <Text>{JSON.stringify(item, null, 2)}</Text> */}
      <MultiSlider
        values={[selectedItem?.min || min, selectedItem?.max || max]}
        sliderLength={180}
        enableLabel={true}
        customLabel={CustomLabel}
        isMarkersSeparated={true}
        trackStyle={styles.trackStyle}
        selectedStyle={styles.slideSelectedStyle}
        markerStyle={styles.markerStyle}
        pressedMarkerStyle={styles.markerPressed}
        min={availableMin || min}
        max={availableMax || max}
        onValuesChangeFinish={(values) => {
          const [minValue, maxValue] = values;
          selectFilter(filterKey, filterKey, {
            type: 'PRICE_RANGE',
            min: minValue,
            max: maxValue,
          });
        }}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  trackStyle: {
    borderRadius: spacing.small,
    height: 2,
  },
  slideSelectedStyle: {
    backgroundColor: color.dark,
  },
  markerStyle: {
    height: 20,
    width: 20,
    borderRadius: spacing.md,
    backgroundColor: color.dark,
    borderWidth: 0.5,
    borderColor: 'transparent',
  },
  markerPressed: {
    backgroundColor: color.demiDark,
  },
  sliderContainer: {
    alignItems: 'center',
    paddingVertical: 2,
  },
  customLabelContainer: {
    position: 'relative',
    justifyContent: 'space-between',
    flexDirection: 'row',
    width: 190,
    left: -4,
    top: 6,
  },
  customLabelText: {
    color: 'black',
    fontSize: 12,
  },
});

export default PriceRange;
  1. Inside filter/assets/FilterIcon.js, add this code
import * as React from 'react';
import Svg, { Path } from 'react-native-svg';

const FilterIcon = (props) => (
  <Svg
    width={16}
    height={16}
    fill="none"
    xmlns="http://www.w3.org/2000/svg"
    {...props}>
    <Path
      d="M11.217 7.277a2.286 2.286 0 0 0 2.223-1.714h1.989a.571.571 0 0 0 0-1.143H13.44a2.285 2.285 0 0 0-4.446 0H.571a.571.571 0 1 0 0 1.143h8.423a2.286 2.286 0 0 0 2.223 1.714Zm-1.143-2.286a1.143 1.143 0 1 1 1.143 1.143 1.143 1.143 0 0 1-1.16-1.143h.017ZM4.286 12.99a2.285 2.285 0 0 0 2.223-1.714h8.92a.571.571 0 0 0 0-1.143h-8.92a2.286 2.286 0 0 0-4.446 0H.57a.571.571 0 1 0 0 1.143h1.492a2.286 2.286 0 0 0 2.223 1.715Zm-1.143-2.285a1.143 1.143 0 1 1 1.143 1.143 1.143 1.143 0 0 1-1.16-1.143h.017Z"
      fill={props.color || '#fff'}
    />
  </Svg>
);

export default FilterIcon;
  1. Inside sort/components/SortButton.js, add this code
import React from 'react';
import { Pressable, StyleSheet, Text, View } from 'react-native';
import Icon from 'react-native-vector-icons/Feather';
import {
  useIsAnySortSelected,
  useSortModal,
} from '@appmaker-xyz/shopify';
import { AppmakerText } from '@appmaker-xyz/uikit';
import SortIcon from '../assets/SortIcon';

export function SortButton({ attributes: { customStyles = {} } = {} }) {
  const {
    activeColor,
    bgColor,
    textColor,
    outline,
    noIcon,
    small,
    wholeContainerStyle,
  } = customStyles;

  const { openSortModal } = useSortModal();
  const isSortApplied = useIsAnySortSelected();

  const containerStyles = [styles.sortButton];
  const activeContainerStyles = [styles.activeContainer];

  if (bgColor) {
    containerStyles.push({ backgroundColor: bgColor });
  }
  if (outline) {
    containerStyles.push({ borderWidth: 1, borderColor: outline });
  }
  if (noIcon) {
    activeContainerStyles.push({ marginLeft: 0 });
  }
  if (small) {
    containerStyles.push({ paddingVertical: 8 });
  }
  if (wholeContainerStyle) {
    containerStyles.push(wholeContainerStyle);
  }

  return (
    <Pressable onPress={openSortModal} style={containerStyles}>
      {noIcon ? null : <SortIcon color={textColor} />}
      <View style={activeContainerStyles}>
        {isSortApplied ? (
          <View
            style={[
              styles.activeDot,
              { backgroundColor: activeColor || '#ff0000' },
            ]}
          />
        ) : null}
        <AppmakerText
          style={styles.sortButtonText}
          fontColor={textColor ? textColor : '#fff'}>
          Sort
        </AppmakerText>
      </View>
    </Pressable>
  );
}

const styles = StyleSheet.create({
  sortButton: {
    flex: 1,
    backgroundColor: '#000',
    paddingVertical: 12,
    flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'center',
  },
  sortButtonText: {
    textAlign: 'center',
    fontSize: 16,
  },
  activeContainer: {
    position: 'relative',
    marginLeft: 6,
  },
  activeDot: {
    position: 'absolute',
    zIndex: 1,
    top: 2,
    right: -16,
    width: 8,
    height: 8,
    borderRadius: 4,
    marginRight: 6,
  },
});

export default SortButton;
  1. Inside sort/SortModal.js, add this code
import { View, Text, StyleSheet, Pressable } from 'react-native';
import React from 'react';
import Modal from 'react-native-modal';
import Icon from '@appmaker-xyz/uikit/Icons/Feather';
import Radio from './components/Radio';

import {
  useSortModal,
  useSortValue,
} from '@appmaker-xyz/shopify';
import { appmaker, appPluginStoreApi } from '@appmaker-xyz/core';
import { useTranslation } from 'react-i18next';
import { AppmakerText } from '@appmaker-xyz/uikit';

function ModalHeader(props) {
  return (
    <View style={styles.topBar}>
      <AppmakerText style={styles.sortHeadText}>Sort By</AppmakerText>
      <Pressable style={styles.back} onPress={props.onClose}>
        <Icon name="x" size={20} color="#000" />
      </Pressable>
    </View>
  );
}
export default function SortModal() {
  const { t } = useTranslation();
  const sortOptions = [
    {
      label: t('Default'),
      value: ' ',
    },
    {
      label: t('Featured'),
      value: 'reverse: false, sortKey: RELEVANCE',
    },
    {
      label: t('Price low to high'),
      value: 'reverse: false, sortKey: PRICE',
    },
    {
      label: t('Price high to low'),
      value: 'reverse: true, sortKey: PRICE',
    },
    {
      label: t('Best Selling'),
      value: 'reverse: false, sortKey: BEST_SELLING',
    },
    {
      label: t('Date, Old To New'),
      value: 'reverse: false, sortKey: CREATED',
    },
    {
      label: t('Date, New To Old'),
      value: 'reverse: true, sortKey: CREATED',
    },
  ];
  const { isSortModalShown, closeSortModal, context } = useSortModal();
  let finalSortOptions = appmaker.applyFilters(
    'shopify-custom-sort-options',
    sortOptions,
    context,
  );
  const pluginSortOptions =
    appPluginStoreApi().getState().plugins['advanced-filter-customization']
      ?.settings?.sort_mappings || [];
  finalSortOptions =
    pluginSortOptions?.length > 0
      ? pluginSortOptions
      : finalSortOptions || sortOptions;
  // const [show, setShow] = React.useState(true);
  const { applySort, selectedSort } = useSortValue();
  const selectedIndex =
    selectedSort && typeof selectedSort === 'string'
      ? finalSortOptions.findIndex((item) => item.value === selectedSort)
      : selectedSort && selectedSort?.id
      ? finalSortOptions.findIndex(
          (item) => item.value?.id === selectedSort?.id,
        )
      : 0;
  return (
    <View>
      <Modal
        isVisible={isSortModalShown}
        style={styles.modal}
        onDismiss={closeSortModal}
        onRequestClose={closeSortModal}>
        <View style={styles.modalContainer}>
          <ModalHeader onClose={closeSortModal} />
          <View style={styles.sortOptions}>
            <Radio
              color="#000000"
              options={finalSortOptions}
              selectedSort={selectedSort}
              initial={selectedIndex}
              onPress={(value) => {
                applySort(value);
              }}
            />
          </View>
        </View>
      </Modal>
    </View>
  );
}

const styles = StyleSheet.create({
  modal: {
    margin: 0,
    justifyContent: 'flex-end',
  },
  modalContainer: {
    backgroundColor: '#fff',
    borderTopLeftRadius: 18,
    borderTopRightRadius: 18,
    overflow: 'hidden',
  },
  topBar: {
    flexDirection: 'row',
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'space-between',
    paddingHorizontal: 12,
    paddingVertical: 8,
    borderBottomColor: '#e2e2e2',
    borderBottomWidth: 0.5,
  },
  sortHeadText: {
    fontFamily: 'NunitoSans-Bold',
    color: '#1B1B1B',
    flexGrow: 1,
  },
  back: {
    padding: 6,
    borderRadius: 18,
  },
  sortOptions: {
    padding: 12,
  },
});
  1. Inside sort/components/Radio.js, add this code
import React from 'react';
import { useState } from 'react';
import { View, StyleSheet, Text, Pressable } from 'react-native';

const RadioItem = (props) => {
  const { label, value, color, activeSort } = props;
  const isActive =
    activeSort === value || (activeSort?.id && activeSort?.id === value?.id);
  const handlePress = () => {
    props?.onPress && props?.onPress(value);
  };

  const themeColor = color || '#000000';

  const borderStyle = {
    borderColor: isActive ? themeColor : `${themeColor}40`,
  };

  const activeBg = { backgroundColor: themeColor };

  return (
    <Pressable style={styles.radioItem} onPress={handlePress}>
      <View style={[styles.radioCircle, borderStyle]}>
        {isActive ? <View style={[styles.selectedRb, activeBg]} /> : null}
      </View>
      <Text style={styles.radioText}>{label}</Text>
    </Pressable>
  );
};

const Radio = (props) => {
  const { color, options, onPress, initial, selectedSort } = props;
  const [activeSort, setActiveSort] = useState(
    selectedSort || options?.[0]?.value || '',
  );
  const onSelect = (value) => {
    setActiveSort(value);
    onPress && onPress(value);
  };
  if (options?.length && options?.length > 0) {
    return options.map((item, id) => {
      return (
        <RadioItem
          key={item.id || id}
          activeSort={activeSort}
          {...item}
          color={color}
          onPress={onSelect}
        />
      );
    });
  }

  return null;
};

const styles = StyleSheet.create({
  radioItem: {
    flexDirection: 'row',
    alignItems: 'center',
    marginBottom: 8,
    paddingVertical: 6,
  },
  radioCircle: {
    height: 20,
    width: 20,
    borderRadius: 100,
    borderWidth: 1.6,
    alignItems: 'center',
    justifyContent: 'center',
  },
  selectedRb: {
    width: 12,
    height: 12,
    borderRadius: 50,
  },

  radioText: {
    marginLeft: 8,
    fontSize: 16,
  },
});

export default Radio;
  1. Inside sort/assets/SortIcon.js, add this code
import React from 'react';
import Svg, { Path } from 'react-native-svg';

const SortIcon = (props) => (
  <Svg
    width={16}
    height={16}
    fill="none"
    xmlns="http://www.w3.org/2000/svg"
    {...props}>
    <Path
      d="m7.26 11.704-2.963 2.963-2.963-2.963m2.963 2.963v-8.89m10.37-1.48-2.963-2.964-2.963 2.963m2.963 5.926V1.333"
      stroke={props.color || '#fff'}
      strokeLinecap="round"
      strokeLinejoin="round"
    />
  </Svg>
);

export default SortIcon;