Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add custom control #1061

Open
karthikCrmwebx opened this issue Jun 10, 2024 · 21 comments
Open

Add custom control #1061

karthikCrmwebx opened this issue Jun 10, 2024 · 21 comments

Comments

@karthikCrmwebx
Copy link

karthikCrmwebx commented Jun 10, 2024

How can we add the value and filed drop down for the type select [in the below code for option set] ?
can we add our custom component to the query builder?

Screenshot 2024-06-10 at 8 20 27 PM Screenshot 2024-06-10 at 8 21 30 PM
import React, { useState, useEffect, ReactPropTypes } from 'react';
import { Utils as QbUtils, Query, Builder, BasicConfig, JsonTree } from '@react-awesome-query-builder/ui';
import '@react-awesome-query-builder/ui/css/styles.css';
import { AntdConfig, AntdWidgets } from '@react-awesome-query-builder/antd';
import './styles.css';
import { EntityFields } from './types';
import { useDispatch } from 'react-redux';
import { getOptionSet } from 'src/store/optionSet';
import { checkFieldIsOfAnyOptionSet } from 'src/Config/CommonUtils';
interface QueryBuilderProps {
  fields: EntityFields;
  onQuery: (query: string) => void;
  jsonTree?: JsonTree;
  saveJsonTree: (jsonTree: JsonTree) => void;
  rebuildBuilder?: boolean;
}
// or import '@react-awesome-query-builder/ui/css/compact_styles.css';
const InitialConfig = BasicConfig;
const queryValue: any = { id: QbUtils.uuid(), type: 'group' };
const config: BasicConfig = {
  ...InitialConfig,
  settings: {
    ...InitialConfig.settings,
    renderField: (props: any) => <AntdWidgets.FieldTreeSelect {...props} />,
  },
};
const renderBuilder = (props: any) => (
  <div className='query-builder-container' style={{ padding: '10px' }}>
    <div className='query-builder qb-lite'>
      <Builder {...props} />
    </div>
  </div>
);
const columnTypes: { [key: string]: string } = {
  bigint: 'number',
  bit: 'boolean',
  datetime: 'datetime',
  decimal: 'number',
  nvarchar: 'text',
  'nvarchar(250)': 'text',
  uniqueidentifier: 'text',

};

const controlTypes :{ [key: string]: string } = {
  OptionSet : 'select',
  MultiSelectionOptionSet:'select',
  TwoOptions:'select'
}

const QueryBuilder = ({ fields, jsonTree, onQuery, saveJsonTree, rebuildBuilder }: QueryBuilderProps) => {  
  const [fieldsData, setFieldsData] = useState({ ...config });



  useEffect(() => {

    const queryFields = Object.entries(fields).reduce(
       (result, [entityTableName, data]) => ({
        ...result,
        [`[${entityTableName}]`]: {
          label: data.entityName,
          type: '!struct',
          subfields: data.fields.reduce(
             (result, field) => {              
              return {
                ...result,
                [`[${field.fieldName}]`]: {
                  type: checkFieldIsOfAnyOptionSet(field.controlType)
                    ? controlTypes[field.controlType]
                    : columnTypes[(field.columnType || field.controlType).toLowerCase()],
                  label: field.displayName,
                  fieldSettings: checkFieldIsOfAnyOptionSet(field.controlType)
                    ? {
                        listValues: field?.fieldSettings
                        ,
                      }
                    : {},
                  valueSources: checkFieldIsOfAnyOptionSet(field.controlType)
                    ? ["value"]
                    : undefined,
                },
              };},
            {}
          ),
        },
      }),
      {}
    );
    setFieldsData((prev: any) => ({ ...prev, fields: { ...queryFields } }));
  }, [fields]);
  useEffect(() => {
    setFstate({
      tree: QbUtils.checkTree(QbUtils.loadTree(jsonTree || queryValue), fieldsData),
      config: fieldsData,
    });
  }, [fieldsData, rebuildBuilder]);
  const [fstate, setFstate] = useState({
    tree: QbUtils.checkTree(QbUtils.loadTree(jsonTree || queryValue), fieldsData),
    config: fieldsData,
  });
  const onChange = (immutableTree: any, config: any) => {
    // Tip: for better performance you can apply `throttle` - see `examples/demo`
    // setFstate({tree: immutableTree, config: config});

    const str = QbUtils.sqlFormat(immutableTree, config) || '';
    onQuery(str);
    const jsonTree = QbUtils.getTree(immutableTree);
    saveJsonTree(jsonTree);
    // `jsonTree` can be saved to backend, and later loaded to `queryValue`
  };

  return (
    <div className='query-wrapper'>
      {fieldsData.fields && (
        <Query {...fieldsData} value={fstate.tree} onChange={onChange} renderBuilder={renderBuilder} />
      )}
    </div>
  );
};
export default QueryBuilder;

@ukrbublik
Copy link
Owner

Do you want to add value/type switcher (as on 2nd screenshot) for "custom drop down" (on screenshot 1)?

Or completely different component, not built-in value/type switcher?

@karthikCrmwebx
Copy link
Author

karthikCrmwebx commented Jun 10, 2024

both are required for our use cases.

@ukrbublik
Copy link
Owner

As for 1st case, there should be 2+ fields of type select in your config. And you should use valueSources: ["value", "field"] , not valueSources: ["value"]

@karthikCrmwebx
Copy link
Author

can you elaborate more or give give some sample since getting "Object literal may only specify known properties, but 'valueSources' does not exist in type 'SelectWidget<Config, SelectWidgetProps>'. Did you mean to write 'valueSrc'?ts(2561)
index.d.ts(1241, 3): The expected type comes from property 'select' which is declared here on type 'CoreWidgets'"

@ukrbublik
Copy link
Owner

I mean these lines in your code

valueSources: checkFieldIsOfAnyOptionSet(field.controlType)
                ? ["value"]
                : undefined,

Please try without it

@karthikCrmwebx
Copy link
Author

tried but still getting same

@ukrbublik
Copy link
Owner

Could you please create a sandbox to reproduce your issue?
You can take this as example
https://github.com/ukrbublik/react-awesome-query-builder/tree/master/packages/sandbox_simple

@karthikCrmwebx
Copy link
Author

how about adding our custom component to the query builder?

@ukrbublik
Copy link
Owner

You can use renderBeforeWidget in config.settings

@karthikCrmwebx
Copy link
Author

any example can you share

@karthikCrmwebx
Copy link
Author

karthikCrmwebx commented Jun 18, 2024

how can we add valueSources : ["value", "field", "ourCustomComponent"] on select of ourCustomComponent, my components should render

@ukrbublik
Copy link
Owner

ukrbublik commented Jun 18, 2024

valueSources array can only have values: value, field, func

@ukrbublik ukrbublik reopened this Jun 18, 2024
@ukrbublik
Copy link
Owner

Please share a sandbox to demonstrate your issue and requested change

@karthikCrmwebx
Copy link
Author

https://codesandbox.io/p/sandbox/modest-tdd-ywnc6g?file=%2Fsrc%2Fdemo%2Findex.jsx

click on add rule and select optSet values

@ukrbublik
Copy link
Owner

If you want to compare [optset_values] with other field, you should have any other field in config with type select, eg:
Screenshot 2024-06-19 at 18 33 38

@ukrbublik
Copy link
Owner

You can render custom select like this:

  renderBeforeWidget: (props) => {
    const {
      selectedField,
      selectedOperator,
    } = props;
    if (selectedField?.includes("[optset_values]") && selectedOperator) {
      return (
        <select
          onChange={({ target: { value } }) => {
            console.log(value);
          }}
        >
          <option value="one">one</option>
          <option value="two">two</option>
        </select>
      );
    }
  },

But right now there is no way to store meta data for rule in store (as you can inspect from the props of renderBeforeWidget).
I can add support of this in the future.
If it suits you, could you describe your case? Like why do you need to display custom select before the value widget, how do you plan to use its value, store its value, what format?
Thanks

@karthikCrmwebx
Copy link
Author

karthikCrmwebx commented Jun 19, 2024

thanks for that.

if we have render renderBeforeWidget:(props: any) => <CustomComponent {...props} />
can we stop rendering the highlighted value and enter value
in the same sandbox

Screenshot 2024-06-19 at 9 23 19 PM

@ukrbublik
Copy link
Owner

No. But instead of renderBeforeWidget you can use a custom component (widget) for your field [optset_values]

@ukrbublik
Copy link
Owner

https://codesandbox.io/p/sandbox/broken-flower-r9hqf9?file=%2Fsrc%2Fdemo%2Fconfig.jsx%3A109%2C20

// use widget select_custom
const fields = {
...
      "[optset_values]": {
        type: "select",
        preferWidgets: ["select_custom"],
...
};

// add widget select_custom
const widgets = {
...
  select_custom: {
    type: "select",
    factory: (props) => {
      console.log("select_custom prtops", props);
      return (
        <div>
          {"custom select"}
          <select
            value={props.value}
            onChange={({ target: { value } }) => {
              props.setValue(value);
            }}
          >
            {props.listValues.map(({ value, title }) => (
              <option value={value}>{title}</option>
            ))}
          </select>
        </div>
      );
    },
  },
};

// allow operators for widget select_custom
const types = {
...
  select: merge({}, InitialConfig.types.select, {
    widgets: {
      select_custom: {
        operators: ["select_equals"],
      },
    },
  }),
};

@karthikCrmwebx
Copy link
Author

cannot access your sandbox can you provide access

@karthikCrmwebx
Copy link
Author

how can we have multi-select

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants