Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Discussion options

Hey!

I tried to add some custom styled buttons to the rules/groups like this:
Képernyőfotó 2025-10-15 - 15 31 48

All my button components following this structure:

export const createRemoveButton = (label: string, className: string, isGroup: boolean) => { return ({ handleOnClick, disabled }: ActionProps) => { return ( <Tooltip title={Remove ${label}}> <IconButton onClick={handleOnClick} disabled={disabled} className={${className} icon-btn} data-testid={className} > {isGroup ? <DeleteSweepIcon sx={{ fontSize: 'large' }} /> : <DeleteIcon className="icon" />} </IconButton> </Tooltip> ); }; };

My question, is there a way to use a single menu button to bring up this three options? See pic for reference:
Képernyőfotó 2025-10-15 - 15 32 23

Thanks in advance!

You must be logged in to vote

There's no built-in way to do this today, but I love the idea! I'll think about how to facilitate this kind of behavior in a more "native" way.

In the meantime what I'd recommend is overriding removeRuleAction and rendering the menu with all three buttons instead of just the "remove" button.

Here's a sandbox where I replaced the rule remove button with a poor man's "menu" using some cheeky CSS on a <details> element:

The styles aren't really important, but the component logic should give you some ideas.

RuleActionMenu…

Replies: 1 comment · 1 reply

Comment options

There's no built-in way to do this today, but I love the idea! I'll think about how to facilitate this kind of behavior in a more "native" way.

In the meantime what I'd recommend is overriding removeRuleAction and rendering the menu with all three buttons instead of just the "remove" button.

Here's a sandbox where I replaced the rule remove button with a poor man's "menu" using some cheeky CSS on a <details> element:

The styles aren't really important, but the component logic should give you some ideas.

RuleActionMenu.tsx
import {
  ActionElement,
  defaultTranslations,
  findPath,
  getParentPath,
  move,
  standardClassnames,
  update,
  useQueryBuilderQuery,
  type ActionProps,
} from 'react-querybuilder';

export const RuleActionMenu = (props: ActionProps) => {
  const clone = () => {
    const newPath = [...getParentPath(props.path), props.path.at(-1)! + 1];
    props.schema.dispatchQuery(
      move(props.schema.getQuery(), props.path, newPath, {
        clone: true,
        combinators: props.schema.combinators,
      })
    );
  };
  const lock = () => {
    props.schema.dispatchQuery(
      update(props.schema.getQuery(), 'disabled', !props.disabled, props.path)
    );
  };
  return (
    <details>
      {/* See ./styles.css for styling */}
      <summary></summary>
      <div>
        {/* Clone button */}
        <ActionElement
          {...props}
          label={defaultTranslations.cloneRule.label}
          title={defaultTranslations.cloneRule.title}
          className={standardClassnames.cloneRule}
          ruleOrGroup={props.ruleOrGroup}
          handleOnClick={clone}
        />
        {/* Delete button (default, so just spread `props` as is) */}
        <ActionElement {...props} />
        {/* Lock button */}
        <ActionElement
          {...props}
          label={defaultTranslations.lockRule.label}
          title={defaultTranslations.lockRule.title}
          className={standardClassnames.lockRule}
          ruleOrGroup={props.ruleOrGroup}
          handleOnClick={lock}
          disabledTranslation={
            findPath(getParentPath(props.path), useQueryBuilderQuery(props))
              ?.disabled
              ? undefined
              : defaultTranslations.lockRuleDisabled
          }
        />
      </div>
    </details>
  );
};
styles.css
@import 'react-querybuilder/dist/query-builder.css';

body {
  margin: 0.5rem !important;
}

code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
}

details,
details > div {
  display: flex;
}

details > div {
  gap: var(--rqb-spacing);
}

details summary {
  list-style: none; /* Hide the default marker */
  cursor: pointer;
}

details summary::before {
  content: '☰';
  display: inline-block;
  margin-right: 0.5em;
  transition: transform 0.2s;
}

details[open] summary::before {
  transform: rotate(-90deg);
}
You must be logged in to vote
1 reply
@KaralyosBela
Comment options

Sorry for late reply, I did what i wanted with your help! Thanks

Answer selected by KaralyosBela
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
🙏
Q&A
Labels
None yet
2 participants
Morty Proxy This is a proxified and sanitized view of the page, visit original site.