diff --git a/packages/ui/package.json b/packages/ui/package.json
index ae4dd3ca..12c50955 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -85,6 +85,7 @@
"@tw-classed/core": "1.4.4",
"@tw-classed/react": "1.4.4",
"ariakit": "2.0.0-next.43",
+ "arktype": "1.0.14-alpha",
"cal-sans": "^1.0.1",
"change-case": "^4.1.2",
"clsx": "^1.2.1",
@@ -104,6 +105,7 @@
"react-is": "^18.2.0",
"react-merge-refs": "^2.0.1",
"react-plock": "^3.0.2",
+ "react-querybuilder": "^6.4.1",
"react-resizable-panels": "^0.0.47",
"react-responsive": "^9.0.2",
"react-stately": "^3.22.0",
diff --git a/packages/ui/src/components/Icon/DragIndicatorIcon.tsx b/packages/ui/src/components/Icon/DragIndicatorIcon.tsx
new file mode 100644
index 00000000..e4026a61
--- /dev/null
+++ b/packages/ui/src/components/Icon/DragIndicatorIcon.tsx
@@ -0,0 +1,10 @@
+import { svgIcon } from './SvgIconWrapper';
+
+export const DragIndicatorIcon = svgIcon(
+ ,
+ {
+ autoSize: true,
+ }
+);
diff --git a/packages/ui/src/components/QueryBuilder/QueryBuilder.context.tsx b/packages/ui/src/components/QueryBuilder/QueryBuilder.context.tsx
new file mode 100644
index 00000000..3d8d1e51
--- /dev/null
+++ b/packages/ui/src/components/QueryBuilder/QueryBuilder.context.tsx
@@ -0,0 +1,6 @@
+import { useContext } from 'react';
+import { QueryBuilderContext } from 'react-querybuilder';
+
+export function useQueryBuilderContext() {
+ return useContext(QueryBuilderContext);
+}
diff --git a/packages/ui/src/components/QueryBuilder/QueryBuilder.mdx b/packages/ui/src/components/QueryBuilder/QueryBuilder.mdx
new file mode 100644
index 00000000..272a5b83
--- /dev/null
+++ b/packages/ui/src/components/QueryBuilder/QueryBuilder.mdx
@@ -0,0 +1,11 @@
+import { ArgTypes, Canvas, Description, Meta, Stories } from '@storybook/blocks';
+
+import * as QueryBuilderStories from './QueryBuilder.stories.tsx';
+
+
+
+# QueryBuilder
+
+
+
+
diff --git a/packages/ui/src/components/QueryBuilder/QueryBuilder.stories.tsx b/packages/ui/src/components/QueryBuilder/QueryBuilder.stories.tsx
new file mode 100644
index 00000000..02237e93
--- /dev/null
+++ b/packages/ui/src/components/QueryBuilder/QueryBuilder.stories.tsx
@@ -0,0 +1,79 @@
+import type { Meta, StoryObj } from '@storybook/react';
+import { SimpleList } from 'components/SimpleList';
+import { useState } from 'react';
+import { Paper } from 'components/Paper';
+import { ReactQueryBuilder } from 'components/QueryBuilder/ReactQueryBuilder';
+import { Button } from 'components/Button';
+import {
+ QueryBuilder,
+ QueryBuilderField,
+ QueryBuilderProvider,
+ QueryBuilderRuleGroupType,
+ QueryBuilderRuleGroupTypeIC,
+} from '.';
+
+const meta = {
+ args: {},
+ title: 'Input/QueryBuilder',
+} satisfies Meta;
+
+export default meta;
+
+const fields: QueryBuilderField[] = [
+ { name: 'firstName', label: 'First Name' },
+ { name: 'lastName', label: 'Last Name' },
+];
+
+const initialQuery: QueryBuilderRuleGroupType = {
+ combinator: 'AND',
+ rules: [
+ { field: 'firstName', operator: 'beginsWith', value: 'Stev' },
+ { field: 'lastName', operator: 'in', value: 'Vai,Vaughan' },
+ ],
+};
+
+type Story = StoryObj;
+
+// export const Default: Story = {
+// args: {},
+// render: function QueryBuilderExample() {
+// const [query, setQuery] = useState(initialQuery);
+
+// return (
+//
+//
+//
+// setQuery(q)}
+// />
+//
+//
+//
+// );
+// },
+// };
+
+export const Default: Story = {
+ args: {},
+ render: function QueryBuilderExample() {
+ const [update, setUpdate] = useState(0);
+ return (
+ <>
+ {' '}
+
+
+ >
+ );
+ },
+};
diff --git a/packages/ui/src/components/QueryBuilder/QueryBuilder.tsx b/packages/ui/src/components/QueryBuilder/QueryBuilder.tsx
new file mode 100644
index 00000000..71c6b428
--- /dev/null
+++ b/packages/ui/src/components/QueryBuilder/QueryBuilder.tsx
@@ -0,0 +1,56 @@
+import './react-querybuilder.css';
+import { queryBuilderComponents } from 'components/QueryBuilder/components';
+import { merge } from 'lodash-es';
+import type {
+ QueryBuilderProps,
+ RuleGroupType as QueryBuilderRuleGroupType,
+ RuleGroupTypeIC as QueryBuilderRuleGroupTypeIC,
+ Field as QueryBuilderField,
+ Option as QueryBuilderOption,
+ Operator as QueryBuilderOperator,
+} from 'react-querybuilder';
+
+import {
+ QueryBuilder as RQBQueryBuilder,
+ getCompatContextProvider as getRQBCompatContextProvider,
+} from 'react-querybuilder';
+
+export {
+ QueryBuilderRuleGroupType,
+ QueryBuilderRuleGroupTypeIC,
+ QueryBuilderField,
+ QueryBuilderOption,
+ QueryBuilderOperator,
+ QueryBuilderProps,
+};
+
+type QueryBuilderRuleGroupVariant = QueryBuilderRuleGroupType | QueryBuilderRuleGroupTypeIC;
+
+const defaultProps = {
+ autoSelectField: true,
+ resetOnFieldChange: true,
+ enableMountQueryChange: false,
+ // addRuleToNewGroups: false,
+
+ // showCombinatorsBetweenRules: true,
+ autoSelectOperator: true,
+ enableDragAndDrop: true,
+ translations: {
+ addRule: { label: '+ Add Conditon', title: 'Add Condition' },
+ },
+} satisfies Partial>;
+
+export const QueryBuilderProvider = getRQBCompatContextProvider({
+ key: 'antribute-query-builder',
+ controlElements: queryBuilderComponents,
+});
+
+const getQueryBuilderProps = >(
+ props: T
+) => merge({}, defaultProps, props);
+
+export const QueryBuilder = (
+ props: QueryBuilderProps
+) => {
+ return ;
+};
diff --git a/packages/ui/src/components/QueryBuilder/ReactQueryBuilder.helpers.ts b/packages/ui/src/components/QueryBuilder/ReactQueryBuilder.helpers.ts
new file mode 100644
index 00000000..f3ea2af4
--- /dev/null
+++ b/packages/ui/src/components/QueryBuilder/ReactQueryBuilder.helpers.ts
@@ -0,0 +1,20 @@
+/* eslint-disable @typescript-eslint/ban-types */
+
+const types = {
+ string: 'string',
+ number: 'number',
+ boolean: 'boolean',
+ object: 'object',
+ undefined: 'undefined',
+ function: 'function',
+ symbol: 'symbol',
+ bigint: 'bigint',
+} as const;
+
+type Type = keyof typeof types;
+
+function getType(value: T): Type {
+ return typeof value as Type;
+}
+
+const c = typeof 'v';
diff --git a/packages/ui/src/components/QueryBuilder/ReactQueryBuilder.tsx b/packages/ui/src/components/QueryBuilder/ReactQueryBuilder.tsx
new file mode 100644
index 00000000..cca7fc93
--- /dev/null
+++ b/packages/ui/src/components/QueryBuilder/ReactQueryBuilder.tsx
@@ -0,0 +1,85 @@
+import { Type, narrow, type } from 'arktype';
+
+export type CondType = 'number' | 'string' | 'array' | 'boolean';
+
+export type GroupOperator = '$and' | '$or';
+
+// Based on these conditions
+export type Condition = '$gt' | '$gte' | '$lte' | '$lt' | '$eq' | '$ne' | '$in' | '$nin';
+
+const equality = (of: Type) => type(['===', of]);
+
+const OperatorSymbolSchema = type(
+ '"$gt" | "$gte" | "$lte" | "$lt" | "$eq" | "$ne" | "$in" | "$nin"'
+);
+
+console.log('OperatorSymbolSchema', OperatorSymbolSchema);
+
+const conditions = {
+ greaterThan: {
+ symbol: '$gt',
+ phrase: 'greater than',
+ operator: '>',
+ typeValidation: {
+ number: (v: number) => type(`number>${v}`),
+ },
+ },
+ greaterThanOrEqual: {
+ symbol: '$gte',
+ phrase: 'greater than or equal to',
+ operator: '>=',
+ typeValidation: {
+ number: (v: number) => type(`number>=${v}`),
+ },
+ },
+ lessThan: {
+ symbol: '$lt',
+ phrase: 'less than',
+ operator: '<',
+ typeValidation: {
+ number: (v: number) => type(`number<${v}`),
+ },
+ },
+ lessThanOrEqual: {
+ symbol: '$lte',
+ phrase: 'less than or equal to',
+ operator: '<=',
+ typeValidation: {
+ number: (v: number) => type(`number<=${v}`),
+ },
+ },
+ equal: {
+ symbol: '$eq',
+ phrase: 'equal to',
+ operator: '===',
+ typeValidation: {
+ number: (v: number) => type(['===', v]),
+ string: (v: string) => type(['===', v]),
+ },
+ },
+ notEqual: {
+ symbol: '$ne',
+ phrase: 'not equal to',
+ operator: '!==',
+ schema: (v: string | number | boolean, options: string[] | number[]) =>
+ narrow(type('string|number'), (v) => options.some((o) => o === v)),
+ },
+ in: {
+ symbol: '$in',
+ phrase: 'in',
+ operator: 'in',
+ schema: (v: string | number) => narrow('()', (s) => s === v),
+ },
+ notIn: {
+ symbol: '$nin',
+ phrase: 'not in',
+ operator: 'not in',
+ },
+} satisfies Record<
+ string,
+ { symbol: string; operator: string; phrase: string } & Record
+>;
+
+export const ReactQueryBuilder = () => {
+ return QueryBuilder
;
+};
diff --git a/packages/ui/src/components/QueryBuilder/components/QueryAction.tsx b/packages/ui/src/components/QueryBuilder/components/QueryAction.tsx
new file mode 100644
index 00000000..1cada3e7
--- /dev/null
+++ b/packages/ui/src/components/QueryBuilder/components/QueryAction.tsx
@@ -0,0 +1,60 @@
+import { Button } from 'components/Button';
+import { Input } from 'components/Input';
+import { ComponentPropsWithoutRef } from 'react';
+import type { ActionWithRulesProps as RQBActionWithRulesProps } from 'react-querybuilder';
+import { generatePropPickerFn } from 'utils';
+
+type QueryActionProps = RQBActionWithRulesProps & ComponentPropsWithoutRef;
+
+const pickRQBActionProps = generatePropPickerFn({
+ rules: '_pick_',
+ label: '_pick_',
+ handleOnClick: '_pick_',
+ disabledTranslation: '_pick_',
+ ruleOrGroup: '_pick_',
+ className: '_pick_',
+ path: '_pick_',
+ level: '_pick_',
+ title: '_pick_',
+ disabled: '_pick_',
+ context: '_pick_',
+ validation: '_pick_',
+ testID: '_pick_',
+ schema: '_pick_',
+});
+
+export const QueryAction = (props: QueryActionProps) => {
+ const rqbActionProps = pickRQBActionProps(props);
+
+ const { disabled } = rqbActionProps;
+
+ const { label, title } = getQueryActionText(rqbActionProps);
+
+ const { onClick, ...componentProps } = pickRQBActionProps.omit(props);
+
+ return (
+
+
+
+ );
+};
+
+QueryAction.displayName = 'QueryAction';
+
+function getQueryActionText({ disabledTranslation, disabled, label }: RQBActionWithRulesProps) {
+ if (disabled && disabledTranslation) {
+ return disabledTranslation;
+ }
+
+ return { label };
+}
diff --git a/packages/ui/src/components/QueryBuilder/components/QueryCombinatorSelector.tsx b/packages/ui/src/components/QueryBuilder/components/QueryCombinatorSelector.tsx
new file mode 100644
index 00000000..9de2cb14
--- /dev/null
+++ b/packages/ui/src/components/QueryBuilder/components/QueryCombinatorSelector.tsx
@@ -0,0 +1,58 @@
+import { Button } from 'components/Button';
+import type { ButtonProps } from 'components/Button';
+import { omit } from 'lodash-es';
+import { ComponentPropsWithoutRef } from 'react';
+import { generatePropPickerFn } from 'utils';
+import type { CombinatorSelectorProps as RQBCombinatorSelectorProps } from 'react-querybuilder';
+import { ToggleGroup, ToggleGroupProps } from 'components/ToggleGroup';
+import { getQueryBuilderOptionsList } from 'components/QueryBuilder/components/helpers';
+import { Input } from 'components/Input';
+
+type QueryActionProps = RQBCombinatorSelectorProps & Omit;
+
+const pickRQBCombinatorSelectorProps = generatePropPickerFn({
+ rules: '_pick_',
+ options: '_pick_',
+ value: '_pick_',
+ handleOnChange: '_pick_',
+ className: '_pick_',
+ path: '_pick_',
+ level: '_pick_',
+ title: '_pick_',
+ disabled: '_pick_',
+ context: '_pick_',
+ validation: '_pick_',
+ testID: '_pick_',
+ schema: '_pick_',
+});
+
+export const QueryCombinatorSelector = (props: QueryActionProps) => {
+ const rqbActionProps = pickRQBCombinatorSelectorProps(props);
+
+ const { options, disabled } = rqbActionProps;
+
+ const optionsList = getQueryBuilderOptionsList(options);
+ const toggleGroupProps = omit(props, pickRQBCombinatorSelectorProps.pickedKeys);
+
+ console.log('COMB', props);
+
+ const path = props.path[props.level - 1];
+
+ const showSelector = path && path > 1;
+
+ return showSelector ? (
+
+ ({ label: e.label, value: e.name }))}
+ disabled={disabled}
+ fullWidth
+ className="max-w-96"
+ variant="outlined"
+ {...toggleGroupProps}
+ />
+
+ ) : null;
+};
+
+QueryCombinatorSelector.displayName = 'QueryCombinatorSelector';
diff --git a/packages/ui/src/components/QueryBuilder/components/QueryDragHandle.tsx b/packages/ui/src/components/QueryBuilder/components/QueryDragHandle.tsx
new file mode 100644
index 00000000..6d2a4046
--- /dev/null
+++ b/packages/ui/src/components/QueryBuilder/components/QueryDragHandle.tsx
@@ -0,0 +1,53 @@
+import { generatePropPickerFn } from 'utils';
+import type { DragHandleProps as RQBDragHandleProps } from 'react-querybuilder';
+import { useQueryBuilderContext } from 'components/QueryBuilder/QueryBuilder.context';
+import { IconButtonProps } from 'components/IconButton';
+import { forwardRef } from 'react';
+import { Input } from 'components/Input';
+import { cx } from 'helpers';
+
+type QueryDragHandleProps = RQBDragHandleProps & IconButtonProps;
+
+const pickRQBDragHandleProps = generatePropPickerFn({
+ label: '_pick_',
+ className: '_pick_',
+ path: '_pick_',
+ level: '_pick_',
+ title: '_pick_',
+ disabled: '_pick_',
+ context: '_pick_',
+ validation: '_pick_',
+ testID: '_pick_',
+ schema: '_pick_',
+});
+
+export const QueryDragHandle = forwardRef(
+ (props, forwardedRef) => {
+ const rqbDragHandleProps = pickRQBDragHandleProps(props);
+
+ const iconButtonProps = pickRQBDragHandleProps.omit(props);
+
+ const { title } = rqbDragHandleProps;
+
+ const { enableDragAndDrop } = useQueryBuilderContext();
+
+ return enableDragAndDrop ? (
+
+
+
+
+
+ ) : null;
+ }
+);
+
+QueryDragHandle.displayName = 'QueryDragHandle';
diff --git a/packages/ui/src/components/QueryBuilder/components/QueryRule.tsx b/packages/ui/src/components/QueryBuilder/components/QueryRule.tsx
new file mode 100644
index 00000000..b1afc538
--- /dev/null
+++ b/packages/ui/src/components/QueryBuilder/components/QueryRule.tsx
@@ -0,0 +1,78 @@
+import { generatePropPickerFn } from 'utils';
+import { ToggleGroupProps } from 'components/ToggleGroup';
+import {
+ useRule,
+ type RuleProps as RQBRuleProps,
+ RuleComponents,
+ useStopEventPropagation,
+ TestID,
+} from 'react-querybuilder';
+
+type QueryRuleProps = RQBRuleProps & Omit;
+
+const pickRQBRuleProps = generatePropPickerFn({
+ isDragging: '_pick_',
+ dragMonitorId: '_pick_',
+ isOver: '_pick_',
+ dropMonitorId: '_pick_',
+ dragRef: '_pick_',
+ dndRef: '_pick_',
+ dropEffect: '_pick_',
+ rule: '_pick_',
+ field: '_pick_',
+ operator: '_pick_',
+ value: '_pick_',
+ valueSource: '_pick_',
+ id: '_pick_',
+ path: '_pick_',
+ parentDisabled: '_pick_',
+ translations: '_pick_',
+ schema: '_pick_',
+ actions: '_pick_',
+ disabled: '_pick_',
+ context: '_pick_',
+});
+
+export const QueryRule = (props: QueryRuleProps) => {
+ const rqbRuleProps = pickRQBRuleProps(props);
+
+ const ruleProps = { ...rqbRuleProps, ...useRule(rqbRuleProps) };
+
+ const {
+ cloneRule,
+ toggleLockRule,
+ removeRule,
+ dragMonitorId,
+ dropMonitorId,
+ dndRef,
+ outerClassName,
+ id,
+ path,
+ } = ruleProps;
+
+ const subComponentProps = {
+ ...ruleProps,
+ ...useStopEventPropagation({ cloneRule, toggleLockRule, removeRule }),
+ };
+
+ const { schema } = subComponentProps;
+
+ const { dragHandle: DragHandle } = schema.controls;
+
+ return (
+
+
+
+ );
+};
+
+QueryRule.displayName = 'QueryRule';
diff --git a/packages/ui/src/components/QueryBuilder/components/QueryValueEditor.tsx b/packages/ui/src/components/QueryBuilder/components/QueryValueEditor.tsx
new file mode 100644
index 00000000..88938ad7
--- /dev/null
+++ b/packages/ui/src/components/QueryBuilder/components/QueryValueEditor.tsx
@@ -0,0 +1,56 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable @typescript-eslint/unbound-method */
+import { Input, InputComponentProps } from 'components/Input';
+import { QueryValueSelector } from 'components/QueryBuilder/components/QueryValueSelector';
+import type { ValueEditorProps, ValueEditorType } from 'react-querybuilder';
+
+type ValueEditorComponentType = Extract;
+
+type ValueEditorComponentMapType = Record<
+ ValueEditorComponentType,
+ (props: ValueEditorProps) => JSX.Element
+>;
+
+const valueEditorComponentMap: ValueEditorComponentMapType = {
+ select: SelectValueEditorField,
+ multiselect: MultiSelectValueEditorField,
+ switch: CheckboxValueEditorField,
+ radio: CheckboxValueEditorField,
+ textarea: TextAreaValueEditorField,
+ text: TextValueEditorField,
+ checkbox: CheckboxValueEditorField,
+};
+
+export const QueryValueEditor = (props: ValueEditorProps) => {
+ const Component = props.type ? valueEditorComponentMap[props.type] : valueEditorComponentMap.text;
+
+ return ;
+};
+
+function SelectValueEditorField({ values, ...props }: ValueEditorProps) {
+ return ;
+}
+
+function MultiSelectValueEditorField({ values, ...props }: ValueEditorProps) {
+ return ;
+}
+
+function CheckboxValueEditorField(props: ValueEditorProps) {
+ return ;
+}
+
+function TextValueEditorField(props: ValueEditorProps) {
+ return ;
+}
+
+function TextAreaValueEditorField(props: ValueEditorProps) {
+ return ;
+}
+
+function getInputComponentProps(props: ValueEditorProps) {
+ return {
+ placeholder: props.title,
+ // isDisabled: props.disabled,
+ onChange: props.handleOnChange,
+ } satisfies InputComponentProps & { onChange: (v: any) => void };
+}
diff --git a/packages/ui/src/components/QueryBuilder/components/QueryValueSelector.tsx b/packages/ui/src/components/QueryBuilder/components/QueryValueSelector.tsx
new file mode 100644
index 00000000..b0eb1cc0
--- /dev/null
+++ b/packages/ui/src/components/QueryBuilder/components/QueryValueSelector.tsx
@@ -0,0 +1,102 @@
+/* eslint-disable @typescript-eslint/unbound-method */
+import {
+ type VersatileSelectorProps,
+ type OptionList,
+ isOptionGroupArray,
+} from 'react-querybuilder';
+// import { toOptions } from 'react-querybuilder';
+import { Combobox, ComboboxProps } from 'components/Combobox';
+import { generatePropPickerFn } from 'utils';
+import { omit } from 'lodash-es';
+import { getQueryBuilderOptionsList } from 'components/QueryBuilder/components/helpers';
+
+type QueryValueSelectorComboboxProps = Omit<
+ ComboboxProps,
+ | 'options'
+ | 'value'
+ | 'getOptionLabel'
+ | 'onValueChange'
+ | 'isMultiSelect'
+ | 'renderOption'
+ | 'getOptionValue'
+>;
+
+type QueryValueSelectorProps = VersatileSelectorProps & QueryValueSelectorComboboxProps;
+
+const pickRQBValueSelectorProps = generatePropPickerFn({
+ value: '_pick_',
+ field: '_pick_',
+ title: '_pick_',
+ path: '_pick_',
+ className: '_pick_',
+ disabled: '_pick_',
+ multiple: '_pick_',
+ options: '_pick_',
+ operator: '_pick_',
+ rules: '_pick_',
+ handleOnChange: '_pick_',
+ level: '_pick_',
+ context: '_pick_',
+ validation: '_pick_',
+ testID: '_pick_',
+ schema: '_pick_',
+ fieldData: '_pick_',
+ listsAsArrays: '_pick_',
+});
+
+export const QueryValueSelector = (props: QueryValueSelectorProps) => {
+ const { title, multiple, value, options, handleOnChange } = pickRQBValueSelectorProps(props);
+
+ console.log('PROPS', props);
+ const optionList = getQueryBuilderOptionsList(options);
+
+ const comboboxProps = pickRQBValueSelectorProps.omit(props);
+
+ const isFieldSelector = props.testID === 'fields';
+
+ const fieldSelectorProps = isFieldSelector
+ ? ({
+ border: false,
+ clearable: false,
+ filled: false,
+ shadow: false,
+ } satisfies Partial)
+ : undefined;
+
+ const common = {
+ placeholder: title,
+ clearable: false,
+ ...fieldSelectorProps,
+
+ ...(comboboxProps as object),
+ } satisfies Partial;
+
+ if (multiple) {
+ const multiValue = value ? value.split(',') : undefined;
+
+ return (
+ e.label)}
+ getOptionLabel={(e) => e}
+ onValueChange={(e) => {
+ handleOnChange(e.join(','));
+ }}
+ />
+ );
+ }
+
+ return (
+ e}
+ options={optionList.map((e) => e.label)}
+ onValueChange={(e) => {
+ handleOnChange(e);
+ }}
+ />
+ );
+};
diff --git a/packages/ui/src/components/QueryBuilder/components/helpers.ts b/packages/ui/src/components/QueryBuilder/components/helpers.ts
new file mode 100644
index 00000000..3563b519
--- /dev/null
+++ b/packages/ui/src/components/QueryBuilder/components/helpers.ts
@@ -0,0 +1,9 @@
+import { OptionList, isOptionGroupArray } from 'react-querybuilder';
+
+export function getQueryBuilderOptionsList(arr: OptionList) {
+ if (isOptionGroupArray(arr)) {
+ return [];
+ }
+
+ return arr;
+}
diff --git a/packages/ui/src/components/QueryBuilder/components/index.ts b/packages/ui/src/components/QueryBuilder/components/index.ts
new file mode 100644
index 00000000..a8e7ca27
--- /dev/null
+++ b/packages/ui/src/components/QueryBuilder/components/index.ts
@@ -0,0 +1 @@
+export * from './query-builder-components';
diff --git a/packages/ui/src/components/QueryBuilder/components/query-builder-components.ts b/packages/ui/src/components/QueryBuilder/components/query-builder-components.ts
new file mode 100644
index 00000000..ac070329
--- /dev/null
+++ b/packages/ui/src/components/QueryBuilder/components/query-builder-components.ts
@@ -0,0 +1,28 @@
+import type { Controls } from 'react-querybuilder';
+import { QueryValueEditor } from 'components/QueryBuilder/components/QueryValueEditor';
+import { QueryRule } from 'components/QueryBuilder/components/QueryRule';
+import { QueryAction } from './QueryAction';
+import { QueryValueSelector } from './QueryValueSelector';
+import { QueryCombinatorSelector } from './QueryCombinatorSelector';
+import { QueryDragHandle } from './QueryDragHandle';
+
+// export { QueryAction, QueryValueSelector };
+
+export const queryBuilderComponents = {
+ addGroupAction: QueryAction,
+ addRuleAction: QueryAction,
+ cloneGroupAction: QueryAction,
+ cloneRuleAction: QueryAction,
+ combinatorSelector: QueryCombinatorSelector,
+ dragHandle: QueryDragHandle,
+ fieldSelector: QueryValueSelector,
+ // notToggle: ChakraNotToggle,
+ operatorSelector: QueryValueSelector,
+ lockRuleAction: QueryAction,
+ lockGroupAction: QueryAction,
+ removeGroupAction: QueryAction,
+ removeRuleAction: QueryAction,
+ valueEditor: QueryValueEditor,
+ valueSourceSelector: QueryValueSelector,
+ rule: QueryRule,
+} satisfies Partial;
diff --git a/packages/ui/src/components/QueryBuilder/index.ts b/packages/ui/src/components/QueryBuilder/index.ts
new file mode 100644
index 00000000..916afbda
--- /dev/null
+++ b/packages/ui/src/components/QueryBuilder/index.ts
@@ -0,0 +1 @@
+export * from './QueryBuilder';
diff --git a/packages/ui/src/components/QueryBuilder/react-querybuilder.css b/packages/ui/src/components/QueryBuilder/react-querybuilder.css
new file mode 100644
index 00000000..951b39ac
--- /dev/null
+++ b/packages/ui/src/components/QueryBuilder/react-querybuilder.css
@@ -0,0 +1,18 @@
+.ruleGroup {
+ @apply bg-none flex flex-col gap-8;
+}
+
+.ruleGroup-header {
+ @apply flex items-center flex-nowrap gap-8;
+}
+
+.ruleGroup-body {
+ @apply flex flex-col gap-8;
+}
+
+.rule {
+ @apply flex items-center gap-16;
+}
+
+.queryBuilder {
+}