# custom-filter-react

> A library for filtering data

Latest version **1.6.4** (published 2024-07-23) · ISC license · 0 weekly downloads

## Install

```sh
npm install custom-filter-react
pnpm add custom-filter-react
yarn add custom-filter-react
bun add custom-filter-react
```

## Health

**Score 20/100 (F)** — status: abandoned.

Positive: esm support; no vulnerabilities.

Warnings: low downloads; no types.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 1.6.4 |
| Published | 2024-07-23 |
| First published | 2024-04-23 |
| Weekly downloads | 0 |
| License | ISC |
| TypeScript types | none |
| Module format | ESM + CommonJS |
| Dependencies | 15 |
| Unpacked size | 53.5 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Maintainers | ducktodawn |

## Links

- npm: https://www.npmjs.com/package/custom-filter-react
- Repository: 1.4.0
- npm.io page: https://npm.io/package/custom-filter-react

## Dependencies (15)

- [axios](https://npm.io/package/axios.md) ^1.6.7
- [react](https://npm.io/package/react.md) ^18.2.0
- [jquery](https://npm.io/package/jquery.md) ^3.7.1
- [boostrap](https://npm.io/package/boostrap.md) ^2.0.0
- [recharts](https://npm.io/package/recharts.md) ^2.12.3
- [bootstrap](https://npm.io/package/bootstrap.md) ^5.3.3
- [react-dom](https://npm.io/package/react-dom.md) ^18.2.0
- [react-select](https://npm.io/package/react-select.md) ^5.8.0
- [@mui/material](https://npm.io/package/@mui/material.md) ^5.15.14
- [react-paginate](https://npm.io/package/react-paginate.md) ^8.2.0
- [react-spinners](https://npm.io/package/react-spinners.md) ^0.13.8
- [react-bootstrap](https://npm.io/package/react-bootstrap.md) ^2.10.1
- [@mui/x-data-grid](https://npm.io/package/@mui/x-data-grid.md) ^7.7.0
- [react-dropdown-select](https://npm.io/package/react-dropdown-select.md) ^4.11.2
- [react-rainbow-components](https://npm.io/package/react-rainbow-components.md) ^1.32.0

## Recent versions

- 1.6.4 (latest) — 2024-07-23
- 1.6.3 — 2024-07-15
- 1.6.2 — 2024-07-15
- 1.6.1 — 2024-07-15
- 1.6.0 — 2024-07-15
- 1.5.9 — 2024-07-08
- 1.5.8 — 2024-07-08
- 1.5.7 — 2024-07-08
- 1.5.6 — 2024-07-08
- 1.5.5 — 2024-07-08
- 1.5.4 — 2024-07-08
- 1.5.3 — 2024-07-08
- 1.5.2 — 2024-07-08
- 1.5.1 — 2024-07-08
- 1.5.0 — 2024-07-08
- … 49 more at https://npm.io/package/custom-filter-react/versions

## README

# Props to pass in

## 1. filterConfigurations

- Required to pass in
- Format: Array of objects
- Used for displaying desired filters
- E.g:

```
   const filterConfigurations = [
   {
     type: "Dropdown",
     placeholder: "Select Category",
     multiSelect: true,
     field: "Country",
     dropdownStyles: { border: "1px solid #ccc" },
     className: "custom-dropdown",
     style: { width: "200px" },
   },
   {
     type: "NumberInput",
     range: true,
     field: "Number",
     placeholder: "Number",
   },
   {
     type: "DateInput",
     range: true,
     field: "Dateline",
     formatStyle: "large",
     firstPlaceHolder: "Start Date",
     secondPlaceHolder: "End Date",
     firstLabel: "Select Date Range",
     button: true,
   },
   {
     type: "CheckboxInput",
     field: "Optional",
     label: "Optional",
     color: "success",
   },
   {
     type: "TextField",
     field: "Name",
     placeholder: "Name",
     width: "30",
   },
 ];
```

## 2. url

- Required to pass in
- Format: String
- Used to retrieve data to be displayed
- E.g http://localhost:5000/applyFilter
- Required params in api url:
  - filterValues, page, limit
  - filterValues format after user clicks on apply filter button will be in an object for example:
    - filterValues: { Name: '', Country: [ 'N/A' ] }
  - page is the page number, will be passed in after applying filter
  - limit is the number of items displayed in a page, will be passed in after applying filter
- Expected output of api is (response.data):
  - data: array of objects (table data)
  - totalPages: Number (number of pages in pagination)
  - Example:
  ````
       {
          "data": [
              {
                  "_id": "6614b7c2199743da24dc1a5b",
                  "Country": "N/A",
                  "Number": 2,
                  "Dateline": "2024-01-06T12:30:00.000+00:00",
                  "Name": "Peter"
              },
              {
                  "_id": "6614b7c2199743da24dc1a5d",
                  "Country": "N/A",
                  "Number": 86,
                  "Dateline": "2024-02-06T12:30:00.000+00:00",
                  "Optional": false,
                  "Name": "Aimee"
              },
              {
                  "_id": "6614b7c2199743da24dc1a5e",
                  "Country": "N/A",
                  "Number": 98,
                  "Dateline": "2022-04-06T12:30:00.000+00:00",
                  "Optional": false,
                  "Name": "Chloe"
              },
              {
                  "_id": "661746bb6d176f3a79b9d64c",
                  "Country": "N/A",
                  "Number": 86,
                  "Dateline": "2024-02-06T12:30:00.000+00:00",
                  "Optional": false,
                  "Name": "Marcus"
              },
              {
                  "_id": "6626050a64812c92fa97c5b5",
                  "Country": "N/A",
                  "Number": 86,
                  "Dateline": "2024-02-06T12:30:00.000+00:00",
                  "Optional": false,
                  "Name": "Sarah"
              }
          ],
          "totalPages": 1
      }
  ````
- Example of how api may look like (collectionName is not required to be in req.query):

```
  router.get("/applyFilter", async (req, res) => {
  try {
    let { filterValues, collectionName } = req.query;
    let { page, limit, sortField, sortDirection } = req.query;
    console.log(req.query);
    if (page === undefined) {
      page = 1;
    }
    if (limit === undefined) {
      limit = 5;
    }
    let skip = (page - 1) * limit;
    // Check if the model already exists
    const Model = require(`./models/${collectionName}`);
    // Construct the filter query based on the filterValues
    const filterQuery = {};

    // Check if filterNumber is a valid number
    if (filterValues) {
      if (!isNaN(parseInt(filterValues.Number))) {
        // Use filterNumber in your filter logic
        filterValues.Number = parseInt(filterValues.Number);
      } else {
        // Handle the case where filterNumber is not a valid number
      }
    } else {
      filterValues = {};
    }
    // Iterate over the filterValues object
    for (const [key, value] of Object.entries(filterValues)) {
      // Check if the value is empty or null
      if (value !== "" && value !== null) {
        // For each filter category
        switch (typeof value) {
          case "string":
            if (value.includes(",")) {
              // Construct regex pattern to match both scenarios as a string
              const regexPattern = `^(${value
                .split(",")
                .map((v) => v.trim())
                .join("|")})$`;

              // Use the regex pattern in the MongoDB query
              filterQuery[key] = { $regex: regexPattern, $options: "i" };
            } else {
              if (value === "false" || value === "true") {
                filterQuery[key] = value;
              } else {
                // Otherwise, construct the regex pattern
                const regex = new RegExp(value, "i");
                filterQuery[key] = { $regex: regex };
              }
            }
            break;
          case "number":
            // For single value filters, directly add to filterQuery
            filterQuery[key] = value;
            break;
          case "object":
            // For range filters, construct $gte and $lte conditions
            if ("start" in value && "end" in value) {
              // Handle range filters
              if (
                value !== null &&
                value !== "" &&
                value.start !== "" &&
                value.end !== ""
              ) {
                if (isNaN(value.start) || isNaN(value.end)) {
                  filterQuery[key] = {
                    $gte: value.start + "T00:00:00",
                    $lte: value.end + "T23:59:59",
                  };
                } else {
                  filterQuery[key] = { $gte: value.start, $lte: value.end };
                }
              }
            } else {
              // Handle other object types
              filterQuery[key] = value;
            }
            break;
          default:
            // Handle other filter types if necessary
            break;
        }
      }
    }
    const totalCount = await Model.countDocuments(filterQuery);
    console.log(skip);
    console.log(totalCount);
    if (skip >= totalCount) {
      skip = 0;
    }
    // Calculate the total number of pages
    const totalPages = Math.ceil(totalCount / limit);
    // Perform the query with filterQuery and pagination options
    const data = await Model.find(filterQuery)
      .sort(sortField ? { [sortField]: sortDirection === "asc" ? 1 : -1 } : {})
      .skip(skip) // Skip documents
      .limit(limit); // Limit the number of documents

    // Send the filtered data and total pages as response
    res.json({ data, totalPages });
  } catch (error) {
    res.status(500).json({ message: error.message });
  }
});
```

## 3 collectionName

- Optional
- Used if api url requires in req.query 

## 4 theme

- Optional
- Default theme is light
- Available themes: light, blue, dark, pink, sky, green, purple, red, yellow

# Example Use of Library

```
import React from 'react'
import UseCustomFilters from 'custom-filter-react'
import config from "../config";
const { backendUrl } = config;
const testLibrary = () => {
  const filterConfigurations = [
    {
      type: "Dropdown",
      placeholder: "Select Category",
      multiSelect: true,
      field: "Country",
      dropdownStyles: { border: "1px solid #ccc" },
      className: "custom-dropdown",
      url: `http://${backendUrl}/getFieldOptions`,
    },
    {
      type: "NumberInput",
      range: true,
      field: "Number",
      placeholder: "Number",
    },
    {
      type: "DateInput",
      range: true,
      field: "Dateline",
      formatStyle: "large",
      firstPlaceHolder: "Start Date",
      secondPlaceHolder: "End Date",
      firstLabel: "Select Date Range",
      button: true,
    },
    {
      type: "CheckboxInput",
      field: "Optional",
      label: "Optional",
      color: "success",
    },
    {
      type: "TextField",
      field: "Name",
      placeholder: "Name",
      width: "30",
    },
  ];
  return (
    <div>
      <UseCustomFilters filterConfigurations={filterConfigurations} url={`http://${backendUrl}/applyFilter`} theme={"red"} />
    </div>
  )
}

export default testLibrary
```

# Required fields for each type of filter

1. Number input
   | Props | Value | Required |
   | -------- | ------- | ------- |
   | field | field in the database to be filtered | yes |  
   | range | true/false | no |
   | width | number | no |
   | size | sm/md/lg | no |
   | placeholder | String | no |
   | firstPlaceHolder | String | no |
   | secondPlaceHolder | String | no |

2. Date input
   | Props | Value | Required |
   | -------- | ------- | ------- |
   | field | field in the database to be filtered | yes |  
   | range | true/false | no |
   | width | number | no |
   | formatStyle | small/medium/large | no |
   | placeholder | String | no |
   | firstPlaceHolder | String | no |
   | secondPlaceHolder | String | no |
   | label | String | no |
   | firstLabel | String | no |
   | secondLabel | String | no |

3. Dropdown
   | Props | Value | Required |
   | -------- | ------- | ------- |
   | field | field in the database to be filtered | yes |  
   | url | String | yes |
   | collectionName | String | no if url route already has collection name determined |
   | fontSize | Number | no |
   | placeholder | String | no |
   | width | Number | no |
   | multiSelect | true/false | no |
   | className | String | no |
   | color | String | no |

4. Checkbox input
   | Props | Value | Required |
   | -------- | ------- | ------- |
   | field | field in the database to be filtered | yes |  
   | defaultChecked | true/false | no |
   | size | small/medium/large | no |
   | label | String | no |
   | color | String | no |

5. Textfield input
   | Props | Value | Required |
   | -------- | ------- | ------- |
   | field | field in the database to be filtered | yes |  
   | width | number | no |
   | size | sm/md/lg | no |
   | placeholder | String | no |

---
_Source: https://npm.io/package/custom-filter-react · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
