Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down
10 changes: 10 additions & 0 deletions packages/ui/src/components/Icon/DragIndicatorIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { svgIcon } from './SvgIconWrapper';

export const DragIndicatorIcon = svgIcon(
<svg>
<path d="M17.5 40q-1.45 0-2.475-1.025Q14 37.95 14 36.5q0-1.45 1.025-2.475Q16.05 33 17.5 33q1.45 0 2.475 1.025Q21 35.05 21 36.5q0 1.45-1.025 2.475Q18.95 40 17.5 40Zm13 0q-1.45 0-2.475-1.025Q27 37.95 27 36.5q0-1.45 1.025-2.475Q29.05 33 30.5 33q1.45 0 2.475 1.025Q34 35.05 34 36.5q0 1.45-1.025 2.475Q31.95 40 30.5 40Zm-13-12.5q-1.45 0-2.475-1.025Q14 25.45 14 24q0-1.45 1.025-2.475Q16.05 20.5 17.5 20.5q1.45 0 2.475 1.025Q21 22.55 21 24q0 1.45-1.025 2.475Q18.95 27.5 17.5 27.5Zm13 0q-1.45 0-2.475-1.025Q27 25.45 27 24q0-1.45 1.025-2.475Q29.05 20.5 30.5 20.5q1.45 0 2.475 1.025Q34 22.55 34 24q0 1.45-1.025 2.475Q31.95 27.5 30.5 27.5ZM17.5 15q-1.45 0-2.475-1.025Q14 12.95 14 11.5q0-1.45 1.025-2.475Q16.05 8 17.5 8q1.45 0 2.475 1.025Q21 10.05 21 11.5q0 1.45-1.025 2.475Q18.95 15 17.5 15Zm13 0q-1.45 0-2.475-1.025Q27 12.95 27 11.5q0-1.45 1.025-2.475Q29.05 8 30.5 8q1.45 0 2.475 1.025Q34 10.05 34 11.5q0 1.45-1.025 2.475Q31.95 15 30.5 15Z" />
</svg>,
{
autoSize: true,
}
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { useContext } from 'react';
import { QueryBuilderContext } from 'react-querybuilder';

export function useQueryBuilderContext() {
return useContext(QueryBuilderContext);
}
11 changes: 11 additions & 0 deletions packages/ui/src/components/QueryBuilder/QueryBuilder.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { ArgTypes, Canvas, Description, Meta, Stories } from '@storybook/blocks';

import * as QueryBuilderStories from './QueryBuilder.stories.tsx';

<Meta of={QueryBuilderStories} />

# QueryBuilder

<Description of={QueryBuilderStories} />
<ArgTypes of={QueryBuilderStories} />
<Stories of={QueryBuilderStories} />
79 changes: 79 additions & 0 deletions packages/ui/src/components/QueryBuilder/QueryBuilder.stories.tsx
Original file line number Diff line number Diff line change
@@ -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<typeof SimpleList.Root>;

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<typeof meta>;

// export const Default: Story = {
// args: {},
// render: function QueryBuilderExample() {
// const [query, setQuery] = useState(initialQuery);

// return (
// <div>
// <QueryBuilderProvider>
// <Paper className="w-screen max-w-7xl" border shadow>
// <QueryBuilder
// fields={fields}
// query={query}
// // showCombinatorsBetweenRules
// // independentCombinators
// onQueryChange={(q) => setQuery(q)}
// />
// </Paper>
// </QueryBuilderProvider>
// </div>
// );
// },
// };

export const Default: Story = {
args: {},
render: function QueryBuilderExample() {
const [update, setUpdate] = useState(0);
return (
<>
{' '}
<Button
onClick={() => {
console.log(`${update}`);
setUpdate(update + 1);
}}
>
Click
</Button>
<ReactQueryBuilder />
</>
);
},
};
56 changes: 56 additions & 0 deletions packages/ui/src/components/QueryBuilder/QueryBuilder.tsx
Original file line number Diff line number Diff line change
@@ -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<QueryBuilderProps<QueryBuilderRuleGroupVariant>>;

export const QueryBuilderProvider = getRQBCompatContextProvider({
key: 'antribute-query-builder',
controlElements: queryBuilderComponents,
});

const getQueryBuilderProps = <T extends QueryBuilderProps<QueryBuilderRuleGroupVariant>>(
props: T
) => merge({}, defaultProps, props);

export const QueryBuilder = <TRuleGroupType extends QueryBuilderRuleGroupVariant>(
props: QueryBuilderProps<TRuleGroupType>
) => {
return <RQBQueryBuilder {...getQueryBuilderProps(props)} />;
};
Original file line number Diff line number Diff line change
@@ -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<T>(value: T): Type {
return typeof value as Type;
}

const c = typeof 'v';
85 changes: 85 additions & 0 deletions packages/ui/src/components/QueryBuilder/ReactQueryBuilder.tsx
Original file line number Diff line number Diff line change
@@ -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 = <T,>(of: Type<T>) => 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<string, unknown>
>;

export const ReactQueryBuilder = () => {
return <div>QueryBuilder</div>;
};
60 changes: 60 additions & 0 deletions packages/ui/src/components/QueryBuilder/components/QueryAction.tsx
Original file line number Diff line number Diff line change
@@ -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<typeof Button>;

const pickRQBActionProps = generatePropPickerFn<RQBActionWithRulesProps>({
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 (
<Input.Container>
<Button
title={title}
onClick={(e) => {
props.handleOnClick(e);
onClick?.(e);
}}
disabled={disabled}
{...componentProps}
>
{label}
</Button>
</Input.Container>
);
};

QueryAction.displayName = 'QueryAction';

function getQueryActionText({ disabledTranslation, disabled, label }: RQBActionWithRulesProps) {
if (disabled && disabledTranslation) {
return disabledTranslation;
}

return { label };
}
Loading