Skip to content

ukrbublik/react-awesome-query-builder

master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Code

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
December 28, 2022 03:43
January 8, 2023 22:37
June 18, 2021 22:58
January 12, 2023 12:09
December 28, 2022 03:43
December 28, 2022 03:43
December 28, 2022 03:43
December 28, 2022 03:43
December 28, 2022 03:43
December 28, 2022 03:43
December 28, 2022 03:43
December 28, 2022 03:43
January 12, 2023 12:09
January 8, 2023 22:20
August 1, 2017 13:40
nit
January 10, 2023 13:39
January 12, 2023 12:09
January 8, 2023 22:09
January 8, 2023 22:09
December 28, 2022 03:43
December 28, 2022 03:43

npm Smoke travis codecov antd mui bootstrap fluent demo sandbox TS sandbox JS

User-friendly React component to build queries (filters).

Inspired by jQuery QueryBuilder. Using awesome UI frameworks for widgets: Ant Design, Material-UI, Bootstrap. Now Fluent UI is also supported!

See live demo

Edit in codesandbox

Features

Screenshot

  • Highly configurable
  • Fields can be of type:
    • simple (string, number, bool, date/time/datetime, list)
    • structs (will be displayed in selectbox as tree)
    • custom type (dev should add its own widget component in config for this)
  • Comparison operators can be:
    • binary (== != < > ..)
    • unary (is empty, is null)
    • 'between' (for numbers, dates, times)
    • complex operators like 'proximity'
  • Values of fields can be compared with:
    • values
    • another fields (of same type)
    • function (arguments also can be values/fields/funcs)
  • Reordering (drag-n-drop) support for rules and groups of rules
  • Themes:
  • Export to MongoDb, SQL, JsonLogic, SpEL, ElasticSearch or your custom format
  • Import from JsonLogic, SpEL
  • TypeScript support (see types and demo in TS)

Getting started

From v6 library is divided into packages:

graph LR;
  core((core))-->ui(ui);
  ui-->antd;
  ui-->mui;
  ui-->material;
  ui-->bootstrap;
  ui-->fluent;

ui re-exports from core, other packages re-export from ui. For using this library on frontend you need to install and use only ui (for basic widgets) or one of framework-specific packages (antd / mui / bootstrap / fluent).

For using this library on server-side (Node.js) you need only core. This is useful if you want to pass query value from frontend to backend in JSON format and perform export eg. to SQL on server-side for security reasons.

Example of installation if you use MUI:

npm i @react-awesome-query-builder/mui --save

Note: We use pnpm. If you want to clone this project and run scripts, please install pnpm:

npm install -g pnpm

See basic usage for minimum code example.

See API and config for documentation.

Demo apps

  • pnpm start - demo app with hot reload of demo code and local library code, uses TS, uses complex config to demonstrate anvanced usage.
  • pnpm sandbox-ts - simple demo app, built with Vite, uses TS, uses AntDesign widgets.
  • pnpm sandbox-js - simple demo app, built with Vite, not uses TS, uses vanilla widgets.

Usage

Minimal JavaScript example with class component

import React, {Component} from 'react';

// >>>
import { Utils as QbUtils, Query, Builder, BasicConfig } from '@react-awesome-query-builder/ui';
import '@react-awesome-query-builder/ui/css/styles.css';
// or import '@react-awesome-query-builder/ui/css/compact_styles.css';
const InitialConfig = BasicConfig;
// <<<

// You need to provide your own config. See below 'Config format'
const config = {
  ...InitialConfig,
  fields: {
    qty: {
      label: 'Qty',
      type: 'number',
      fieldSettings: {
        min: 0,
      },
      valueSources: ['value'],
      preferWidgets: ['number'],
    },
    price: {
      label: 'Price',
      type: 'number',
      valueSources: ['value'],
      fieldSettings: {
        min: 10,
        max: 100,
      },
      preferWidgets: ['slider', 'rangeslider'],
    },
    name: {
      label: 'Name',
      type: 'text',
    },
    color: {
      label: 'Color',
      type: 'select',
      valueSources: ['value'],
      fieldSettings: {
        listValues: [
          { value: 'yellow', title: 'Yellow' },
          { value: 'green', title: 'Green' },
          { value: 'orange', title: 'Orange' }
        ],
      }
    },
    is_promotion: {
      label: 'Promo?',
      type: 'boolean',
      operators: ['equal'],
      valueSources: ['value'],
    },
  }
};

// You can load query value from your backend storage (for saving see `Query.onChange()`)
const queryValue = {"id": QbUtils.uuid(), "type": "group"};


class DemoQueryBuilder extends Component {
  state = {
    tree: QbUtils.checkTree(QbUtils.loadTree(queryValue), config),
    config: config
  };
  
  render = () => (
    <div>
      <Query
        {...config} 
        value={this.state.tree}
        onChange={this.onChange}
        renderBuilder={this.renderBuilder}
      />
      {this.renderResult(this.state)}
    </div>
  )

  renderBuilder = (props) => (
    <div className="query-builder-container" style={{padding: '10px'}}>
      <div className="query-builder qb-lite">
        <Builder {...props} />
      </div>
    </div>
  )

  renderResult = ({tree: immutableTree, config}) => (
    <div className="query-builder-result">
      <div>Query string: <pre>{JSON.stringify(QbUtils.queryString(immutableTree, config))}</pre></div>
      <div>MongoDb query: <pre>{JSON.stringify(QbUtils.mongodbFormat(immutableTree, config))}</pre></div>
      <div>SQL where: <pre>{JSON.stringify(QbUtils.sqlFormat(immutableTree, config))}</pre></div>
      <div>JsonLogic: <pre>{JSON.stringify(QbUtils.jsonLogicFormat(immutableTree, config))}</pre></div>
    </div>
  )
  
  onChange = (immutableTree, config) => {
    // Tip: for better performance you can apply `throttle` - see `examples/demo`
    this.setState({tree: immutableTree, config: config});

    const jsonTree = QbUtils.getTree(immutableTree);
    console.log(jsonTree);
    // `jsonTree` can be saved to backend, and later loaded to `queryValue`
  }
}
export default DemoQueryBuilder;

Minimal TypeScript example with function component

import React, { useState, useCallback } from "react";

// >>> 
import type { JsonGroup, Config, ImmutableTree, BuilderProps } from '@react-awesome-query-builder/ui';
import { Utils as QbUtils, Query, Builder, BasicConfig } from '@react-awesome-query-builder/ui';
import '@react-awesome-query-builder/ui/css/styles.css';
// or import '@react-awesome-query-builder/ui/css/compact_styles.css';
const InitialConfig = BasicConfig;
// <<<

// You need to provide your own config. See below 'Config format'
const config: Config = {
  ...InitialConfig,
  fields: {
    qty: {
      label: "Qty",
      type: "number",
      fieldSettings: {
        min: 0
      },
      valueSources: ["value"],
      preferWidgets: ["number"]
    },
    price: {
      label: "Price",
      type: "number",
      valueSources: ["value"],
      fieldSettings: {
        min: 10,
        max: 100
      },
      preferWidgets: ["slider", "rangeslider"]
    },
    name: {
      label: 'Name',
      type: 'text',
    },
    color: {
      label: "Color",
      type: "select",
      valueSources: ["value"],
      fieldSettings: {
        listValues: [
          { value: "yellow", title: "Yellow" },
          { value: "green", title: "Green" },
          { value: "orange", title: "Orange" }
        ]
      }
    },
    is_promotion: {
      label: "Promo?",
      type: "boolean",
      operators: ["equal"],
      valueSources: ["value"]
    }
  }
};

// You can load query value from your backend storage (for saving see `Query.onChange()`)
const queryValue: JsonGroup = { id: QbUtils.uuid(), type: "group" };

const DemoQueryBuilder: React.FC = () => {
  const [state, setState] = useState({
    tree: QbUtils.checkTree(QbUtils.loadTree(queryValue), config),
    config: config
  });

  const onChange = useCallback((immutableTree: ImmutableTree, config: Config) => {
    // Tip: for better performance you can apply `throttle` - see `examples/demo`
    setState(prevState => ({ ...prevState, tree: immutableTree, config: config }));

    const jsonTree = QbUtils.getTree(immutableTree);
    console.log(jsonTree);
    // `jsonTree` can be saved to backend, and later loaded to `queryValue`
  }, []);

  const renderBuilder = useCallback((props: BuilderProps) => (
    <div className="query-builder-container" style={{ padding: "10px" }}>
      <div className="query-builder qb-lite">
        <Builder {...props} />
      </div>
    </div>
  ), []);

  return (
    <div>
      <Query
        {...config}
        value={state.tree}
        onChange={onChange}
        renderBuilder={renderBuilder}
      />
      <div className="query-builder-result">
        <div>
          Query string:{" "}
          <pre>
            {JSON.stringify(QbUtils.queryString(state.tree, state.config))}
          </pre>
        </div>
        <div>
          MongoDb query:{" "}
          <pre>
            {JSON.stringify(QbUtils.mongodbFormat(state.tree, state.config))}
          </pre>
        </div>
        <div>
          SQL where:{" "}
          <pre>
            {JSON.stringify(QbUtils.sqlFormat(state.tree, state.config))}
          </pre>
        </div>
        <div>
          JsonLogic:{" "}
          <pre>
            {JSON.stringify(QbUtils.jsonLogicFormat(state.tree, state.config))}
          </pre>
        </div>
      </div>
    </div>
  );
};
export default DemoQueryBuilder;

API

<Query />

Props:

  • {...config} - destructured CONFIG
  • value - query value in internal Immutable format
  • onChange - callback when query value changed. Params: value (in Immutable format), config, actionMeta (details about action which led to the change, see ActionMeta in index.d.ts).
  • renderBuilder - function to render query builder itself. Takes 1 param props you need to pass into <Builder {...props} />.

Notes:

  • Please apply useCallback for onChange and renderBuilder for performance reason
  • If you put query builder component inside Material-UI's <Dialog /> or <Popover />, please:
    • use prop disableEnforceFocus={true} for dialog or popver
    • set css .MuiPopover-root, .MuiDialog-root { z-index: 900 !important; } (or 1000 for AntDesign v3)
  • If you put query builder component inside Fluent-UI's <Panel />, please:
    • set css .ms-Layer.ms-Layer--fixed.root-119 { z-index: 900 !important; }
  • props arg in renderBuilder have actions and dispatch you can use to run actions programmatically (for list of actions see Actions in index.d.ts).

<Builder />

Render this component only inside Query.renderBuilder() like in example above:

  renderBuilder = (props) => (
    <div className="query-builder-container">
      <div className="query-builder qb-lite">
          <Builder {...props} />
      </div>
    </div>
  )

Wrapping <Builder /> in div.query-builder is necessary.
Optionally you can add class .qb-lite to it for showing action buttons (like delete rule/group, add, etc.) only on hover, which will look cleaner.
Wrapping in div.query-builder-container is necessary if you put query builder inside scrollable block.

Utils

  • Save, load:

    getTree (immutableValue, light = true, children1AsArray = true) -> Object

    Convert query value from internal Immutable format to JS object. You can use it to save value on backend in onChange callback of <Query>.
    Tip: Use light = false in case if you want to store query value in your state in JS format and pass it as value of <Query> after applying loadTree() (which is not recommended because of double conversion). See issue #190

    loadTree (jsValue, config) -> Immutable

    Convert query value from JS format to internal Immutable format. You can use it to load saved value from backend and pass as value prop to <Query> (don't forget to also apply checkTree()).

    checkTree (immutableValue, config) -> Immutable

    Validate query value corresponding to config. Invalid parts of query (eg. if field was removed from config) will be always deleted. Invalid values (values not passing validateValue in config, bad ranges) will be deleted if showErrorMessage is false OR marked with errors if showErrorMessage is true.

    isValidTree (immutableValue) -> Boolean

    If showErrorMessage in config.settings is true, use this method to check is query has bad values.
  • Export:

    queryString (immutableValue, config, isForDisplay = false) -> String

    Convert query value to custom string representation. isForDisplay = true can be used to make string more "human readable".

    mongodbFormat (immutableValue, config) -> Object

    Convert query value to MongoDb query object.

    sqlFormat (immutableValue, config) -> String

    Convert query value to SQL where string.

    spelFormat (immutableValue, config) -> String

    Convert query value to Spring Expression Language (SpEL).

    elasticSearchFormat (immutableValue, config) -> Object

    Convert query value to ElasticSearch query object.

    jsonLogicFormat (immutableValue, config) -> {logic, data, errors}

    Convert query value to JsonLogic format. If there are no errors, logic will be rule object and data will contain all used fields with null values ("template" data).
  • Import:

    loadFromJsonLogic (jsonLogicObject, config) -> Immutable

    Convert query value from JsonLogic format to internal Immutable format.

    _loadFromJsonLogic (jsonLogicObject, config) -> [Immutable, errors]

    loadFromSpel (string, config) -> [Immutable, errors]

    Convert query value from Spring Expression Language (SpEL) format to internal Immutable format.

Config format

This library uses configarion driven aproach. Config defines what value types, operators are supported, how they are rendered, imported, exported. At minimum, you need to provide your own set of fields as in basic usage. See CONFIG for full documentation.

Versions

Version 5 is backward-compatible with 2-4. From version 6 library is divided into packages. It's recommended to update your version to 6.x. You just need to change your imports, see Migration to 6.0.0

Supported versions

Version Supported
6.x
5.x ✔️
4.x ⚠️
3.x
2.x
1.x
0.x

Changelog

See CHANGELOG

Migration to 6.0.0

From version 6 library is divided into packages. Please remove package react-awesome-query-builder and install one of:

Library code is backward-compatible with version 2-5. You just need to change your imports.

- import { Utils, Export, Import, BasicFuncs } from 'react-awesome-query-builder';
+ import { Utils, Export, Import, BasicFuncs } from '@react-awesome-query-builder/ui';

- import { Query, Builder, BasicConfig, Widgets, Operators } from 'react-awesome-query-builder';
+ import { Query, Builder, BasicConfig, VanillaWidgets, CustomOperators } from '@react-awesome-query-builder/ui';

- import AntdConfig from 'react-awesome-query-builder/lib/config/antd';
+ import {AntdConfig} from '@react-awesome-query-builder/antd';
- import MuiConfig from 'react-awesome-query-builder/lib/config/mui';
+ import {MuiConfig} from '@react-awesome-query-builder/mui';
- import MaterialConfig from 'react-awesome-query-builder/lib/config/material';
+ import {MaterialConfig} from '@react-awesome-query-builder/material';
- import BootstrapConfig from 'react-awesome-query-builder/lib/config/bootstrap';
+ import {BootstrapConfig} from '@react-awesome-query-builder/bootstrap';

- import 'react-awesome-query-builder/lib/css/styles.css';
+ import '@react-awesome-query-builder/ui/css/styles.css';
- import 'react-awesome-query-builder/lib/css/compact_styles.css';
+ import '@react-awesome-query-builder/ui/css/compact_styles.css'; // instead of styles.css for more compact look

Note that you should import all types and values from a single package. For example, @react-awesome-query-builder/antd if you use AntDesign - it inherits core and ui:

import {Utils, Query, Builder, AntdConfig} from '@react-awesome-query-builder/antd';

You don't need to install and import ui and core packages in this case, just use antd.
Same for styles - please import from antd package:

import '@react-awesome-query-builder/antd/css/styles.css';

instead of

import '@react-awesome-query-builder/ui/css/styles.css';

If you use vanilla widgets, please install, import and use only @react-awesome-query-builder/ui (it inherits core).

One more thing, if you use Bootstrap widgets, now you need to explicitly import CSS:

import "bootstrap/dist/css/bootstrap.min.css";

Migration to 5.2.0

Breaking change: children1 is now an indexed array (instead of object) in result of Utils.getTree() to preserve items order.
Before:

children1: {
  '<id1>': { type: 'rule', properties: ... },
  '<id2>': { type: 'rule', properties: ... }
}

After:

children1: [
  { id: '<id1>', type: 'rule', properties: ... },
  { id: '<id2>', type: 'rule', properties: ... },
]

Utils.loadTree() is backward comatible with children1 being array or object.
But if you rely on previous format (maybe do post-processing of getTree() result), please use Utils.getTree(tree, true, false) - it will behave same as before this change.

Another breaking change: removeIncompleteRulesOnLoad and removeEmptyGroupsOnLoad now default to true, set them to false in your settings to preserve the behaviour before 5.2.0.

Migration to 4.9.0

Version 4.9.0 has a breaking change for operators is_empty and is_not_empty.
Now these operators can be used for text type only (for other types they will be auto converted to is_null/is_not_null during loading of query value created with previous versions).
Changed meaning of is_empty - now it's just strict comparing with empty string.
Before change the meaning was similar to is_null.
If you used is_empty for text types with intention of comparing with null, please replace is_empty -> is_null, is_not_empty -> is_not_null in saved query values.
If you used JsonLogic for saving, you need to replace {"!": {"var": "your_field"}} -> {"==": [{"var": "your_field"}, null]} and {"!!": {"var": "your_field"}} -> {"!=": [{"var": "your_field"}, null]}.

Migration from v1 to v2

From v2.0 of this lib AntDesign is now optional (peer) dependency, so you need to explicitly include antd (4.x) in package.json of your project if you want to use AntDesign UI.
Please import AntdConfig from react-awesome-query-builder/lib/config/antd and use it as base for your config (see below in usage).
Alternatively you can use BasicConfig for simple vanilla UI, which is by default.
Support of other UI frameworks (like Bootstrap) are planned for future, see Other UI frameworks.

Contributing

Code Contributing

This project exists thanks to all the people who contribute. [Contribute].

Financial Contributing

Become a financial contributor and help us sustain our community. [Contribute]

If you mention in an GitHub issue that you are a sponsor, we will prioritize helping you.

As a sponsor you can ask to implement a feature that is not in a todo list or motivate for faster implementation.

Individuals

Organizations

Support this project with your organization. Your logo will show up here with a link to your website. [Contribute]

License

MIT. See also LICENSE.txt

Forked from https://github.com/fubhy/react-query-builder