From 2229cd03cffe7e1dc100f38766334d2649ecc536 Mon Sep 17 00:00:00 2001 From: Tyler Mitchell Date: Thu, 8 Jun 2023 17:43:12 -0500 Subject: [PATCH] work on query builder --- packages/ui/package.json | 2 + .../src/components/Icon/DragIndicatorIcon.tsx | 10 ++ .../QueryBuilder/QueryBuilder.context.tsx | 6 ++ .../components/QueryBuilder/QueryBuilder.mdx | 11 ++ .../QueryBuilder/QueryBuilder.stories.tsx | 79 ++++++++++++++ .../components/QueryBuilder/QueryBuilder.tsx | 56 ++++++++++ .../QueryBuilder/ReactQueryBuilder.helpers.ts | 20 ++++ .../QueryBuilder/ReactQueryBuilder.tsx | 85 +++++++++++++++ .../QueryBuilder/components/QueryAction.tsx | 60 +++++++++++ .../components/QueryCombinatorSelector.tsx | 58 ++++++++++ .../components/QueryDragHandle.tsx | 53 +++++++++ .../QueryBuilder/components/QueryRule.tsx | 78 ++++++++++++++ .../components/QueryValueEditor.tsx | 56 ++++++++++ .../components/QueryValueSelector.tsx | 102 ++++++++++++++++++ .../QueryBuilder/components/helpers.ts | 9 ++ .../QueryBuilder/components/index.ts | 1 + .../components/query-builder-components.ts | 28 +++++ .../ui/src/components/QueryBuilder/index.ts | 1 + .../QueryBuilder/react-querybuilder.css | 18 ++++ 19 files changed, 733 insertions(+) create mode 100644 packages/ui/src/components/Icon/DragIndicatorIcon.tsx create mode 100644 packages/ui/src/components/QueryBuilder/QueryBuilder.context.tsx create mode 100644 packages/ui/src/components/QueryBuilder/QueryBuilder.mdx create mode 100644 packages/ui/src/components/QueryBuilder/QueryBuilder.stories.tsx create mode 100644 packages/ui/src/components/QueryBuilder/QueryBuilder.tsx create mode 100644 packages/ui/src/components/QueryBuilder/ReactQueryBuilder.helpers.ts create mode 100644 packages/ui/src/components/QueryBuilder/ReactQueryBuilder.tsx create mode 100644 packages/ui/src/components/QueryBuilder/components/QueryAction.tsx create mode 100644 packages/ui/src/components/QueryBuilder/components/QueryCombinatorSelector.tsx create mode 100644 packages/ui/src/components/QueryBuilder/components/QueryDragHandle.tsx create mode 100644 packages/ui/src/components/QueryBuilder/components/QueryRule.tsx create mode 100644 packages/ui/src/components/QueryBuilder/components/QueryValueEditor.tsx create mode 100644 packages/ui/src/components/QueryBuilder/components/QueryValueSelector.tsx create mode 100644 packages/ui/src/components/QueryBuilder/components/helpers.ts create mode 100644 packages/ui/src/components/QueryBuilder/components/index.ts create mode 100644 packages/ui/src/components/QueryBuilder/components/query-builder-components.ts create mode 100644 packages/ui/src/components/QueryBuilder/index.ts create mode 100644 packages/ui/src/components/QueryBuilder/react-querybuilder.css 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 { +}