Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 81e1bb1

Browse files
committedNov 6, 2024
Initial commit
0 parents  commit 81e1bb1

37 files changed

+1459
-0
lines changed
 

‎.gitignore

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Dependencies
2+
node_modules/
3+
4+
# Generated files
5+
build/
6+
dist/
7+
coverage/
8+
build-analysis.*
9+
10+
# Misc
11+
.DS_Store
12+
.env
13+
.env.local
14+
.env.development.local
15+
.env.test.local
16+
.env.production.local
17+
18+
.idea
19+
*.log
20+
.tmp
21+
build-stats.*
22+
23+
.pnp.*
24+
.yarn/*
25+
!.yarn/patches
26+
!.yarn/plugins
27+
!.yarn/releases
28+
!.yarn/sdks
29+
!.yarn/versions
30+
yarn.lock

‎.prettierignore

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Hidden(ish) files
2+
**/.*
3+
4+
# Generated
5+
*Parser.js
6+
7+
# Binaries
8+
*.jpeg
9+
*.jpg
10+
*.png
11+
*.svg
12+
bun.lockb
13+
14+
# Unsupported
15+
*.jison
16+
*.toml
17+
CNAME

‎.vscode/settings.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"editor.formatOnSave": true,
3+
"files.eol": "\n",
4+
"typescript.tsdk": "node_modules/typescript/lib"
5+
}

‎README.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
## @react-querybuilder/chakra2
2+
3+
Official [react-querybuilder](https://npmjs.com/package/react-querybuilder) compatibility package for [Chakra UI version 2](https://chakra-ui.com/).
4+
5+
> [!WARNING] This package is only compatible with Chakra UI version 2
6+
>
7+
> For Chakra UI version 3 compatibility, use [`@react-querybuilder/chakra`](https://npmjs.com/package/@react-querybuilder/chakra).
8+
9+
- [Demo (using latest Chakra UI)](https://react-querybuilder.js.org/demo/chakra)
10+
- [Full documentation](https://react-querybuilder.js.org/)
11+
- [CodeSandbox](https://react-querybuilder.js.org/sandbox?t=chakra2) / [StackBlitz](https://react-querybuilder.js.org/sandbox?p=stackblitz&t=chakra2) example projects
12+
13+
![Screenshot](./screenshot.png)
14+
15+
## Installation
16+
17+
```bash
18+
npm i react-querybuilder @react-querybuilder/chakra @chakra-ui/icons @chakra-ui/react @chakra-ui/system @emotion/react @emotion/styled framer-motion
19+
# OR yarn add / pnpm add / bun add
20+
```
21+
22+
## Usage
23+
24+
To configure the query builder to use Chakra-compatible components, place `QueryBuilderChakra` above `QueryBuilder` and beneath `ChakraProvider` in the component hierarchy.
25+
26+
```tsx
27+
import { ChakraProvider, extendTheme } from "@chakra-ui/react";
28+
import { QueryBuilderChakra } from "@react-querybuilder/chakra";
29+
import { useState } from "react";
30+
import {
31+
type Field,
32+
QueryBuilder,
33+
type RuleGroupType,
34+
} from "react-querybuilder";
35+
36+
const chakraTheme = extendTheme();
37+
38+
const fields: Field[] = [
39+
{ name: "firstName", label: "First Name" },
40+
{ name: "lastName", label: "Last Name" },
41+
];
42+
43+
export function App() {
44+
const [query, setQuery] = useState<RuleGroupType>({
45+
combinator: "and",
46+
rules: [],
47+
});
48+
49+
return (
50+
<ChakraProvider theme={chakraTheme}>
51+
<QueryBuilderChakra>
52+
<QueryBuilder
53+
fields={fields}
54+
defaultQuery={query}
55+
onQueryChange={setQuery}
56+
/>
57+
</QueryBuilderChakra>
58+
</ChakraProvider>
59+
);
60+
}
61+
```
62+
63+
> [!NOTE]
64+
>
65+
> Some additional styling may be necessary. We recommend the following:
66+
>
67+
> ```css
68+
> .queryBuilder .chakra-select__wrapper {
69+
> width: fit-content;
70+
> display: inline-block;
71+
> }
72+
>
73+
> .queryBuilder .chakra-input {
74+
> width: auto;
75+
> display: inline-block;
76+
> }
77+
>
78+
> .queryBuilder .chakra-radio-group {
79+
> display: inline-block;
80+
> }
81+
> ```
82+
83+
`QueryBuilderChakra` is a React context provider that assigns the following props to all descendant `QueryBuilder` elements. The props can be overridden on the `QueryBuilder` or used directly without the context provider.
84+
85+
| Export | `QueryBuilder` prop |
86+
| ----------------------- | ------------------------------- |
87+
| `chakraControlElements` | `controlElements` |
88+
| `chakraTranslations` | `translations` |
89+
| `ChakraActionElement` | `controlElements.actionElement` |
90+
| `ChakraDragHandle` | `controlElements.dragHandle` |
91+
| `ChakraNotToggle` | `controlElements.notToggle` |
92+
| `ChakraValueEditor` | `controlElements.valueEditor` |
93+
| `ChakraValueSelector` | `controlElements.valueSelector` |
94+
95+
> [!TIP]
96+
>
97+
> By default, this package uses icons from `@chakra-ui/icons` for button labels. To reset button labels to their default strings, use `defaultTranslations` from `react-querybuilder`.
98+
>
99+
> ```tsx
100+
> <QueryBuilderChakra translations={defaultTranslations}>
101+
> ```

‎bun.lockb

153 KB
Binary file not shown.

‎dev/constants.ts

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import type { BaseOption, Field, RuleGroupType, RuleGroupTypeIC } from 'react-querybuilder';
2+
import { convertToIC, defaultOperators, generateID } from 'react-querybuilder';
3+
import { musicalInstruments } from './musicalInstruments';
4+
5+
const dos = defaultOperators as BaseOption[];
6+
7+
export const fields: Field[] = [
8+
{
9+
name: 'firstName',
10+
label: 'First Name',
11+
placeholder: 'Enter first name',
12+
},
13+
{
14+
name: 'lastName',
15+
label: 'Last Name',
16+
placeholder: 'Enter last name',
17+
defaultOperator: 'beginsWith',
18+
},
19+
{ name: 'age', label: 'Age', inputType: 'number' },
20+
{
21+
name: 'isMusician',
22+
label: 'Is a musician',
23+
valueEditorType: 'checkbox',
24+
operators: dos.filter(op => op.name === '='),
25+
defaultValue: false,
26+
},
27+
{
28+
name: 'instrument',
29+
label: 'Primary instrument',
30+
valueEditorType: 'select',
31+
values: musicalInstruments,
32+
defaultValue: 'Cowbell',
33+
operators: dos.filter(op => op.name === '=' || op.name === 'in'),
34+
},
35+
{
36+
name: 'alsoPlays',
37+
label: 'Also plays',
38+
valueEditorType: 'multiselect',
39+
values: musicalInstruments,
40+
defaultValue: 'More cowbell',
41+
operators: dos.filter(op => op.name === 'in'),
42+
},
43+
{
44+
name: 'gender',
45+
label: 'Gender',
46+
operators: dos.filter(op => op.name === '='),
47+
valueEditorType: 'radio',
48+
values: [
49+
{ name: 'M', label: 'Male' },
50+
{ name: 'F', label: 'Female' },
51+
{ name: 'O', label: 'Other' },
52+
],
53+
},
54+
{ name: 'height', label: 'Height', inputType: 'number' },
55+
{ name: 'job', label: 'Job' },
56+
{ name: 'email', label: 'Email', inputType: 'email' },
57+
{ name: 'description', label: 'Description', valueEditorType: 'textarea' },
58+
{ name: 'birthdate', label: 'Birth Date', inputType: 'date' },
59+
{ name: 'datetime', label: 'Show Time', inputType: 'datetime-local' },
60+
{ name: 'alarm', label: 'Daily Alarm', inputType: 'time' },
61+
{
62+
name: 'groupedField1',
63+
label: 'Grouped Field 1',
64+
comparator: 'groupNumber',
65+
groupNumber: 'group1',
66+
valueSources: ['field', 'value'],
67+
},
68+
{
69+
name: 'groupedField2',
70+
label: 'Grouped Field 2',
71+
comparator: 'groupNumber',
72+
groupNumber: 'group1',
73+
valueSources: ['field', 'value'],
74+
},
75+
{
76+
name: 'groupedField3',
77+
label: 'Grouped Field 3',
78+
comparator: 'groupNumber',
79+
groupNumber: 'group1',
80+
valueSources: ['field', 'value'],
81+
},
82+
{
83+
name: 'groupedField4',
84+
label: 'Grouped Field 4',
85+
comparator: 'groupNumber',
86+
groupNumber: 'group1',
87+
valueSources: ['field', 'value'],
88+
},
89+
];
90+
91+
export const emptyQuery: RuleGroupType = { combinator: 'and', rules: [] };
92+
export const emptyQueryIC: RuleGroupTypeIC = convertToIC(emptyQuery);
93+
94+
export const initialQuery: RuleGroupType = {
95+
id: generateID(),
96+
combinator: 'and',
97+
not: false,
98+
rules: [
99+
{
100+
id: generateID(),
101+
field: 'firstName',
102+
value: 'Stev',
103+
operator: 'beginsWith',
104+
},
105+
{
106+
id: generateID(),
107+
field: 'lastName',
108+
value: 'Vai, Vaughan',
109+
operator: 'in',
110+
},
111+
{
112+
id: generateID(),
113+
field: 'age',
114+
operator: '>',
115+
value: '28',
116+
},
117+
{
118+
id: generateID(),
119+
combinator: 'or',
120+
rules: [
121+
{
122+
id: generateID(),
123+
field: 'isMusician',
124+
operator: '=',
125+
value: true,
126+
},
127+
{
128+
id: generateID(),
129+
field: 'instrument',
130+
operator: '=',
131+
value: 'Guitar',
132+
},
133+
],
134+
},
135+
{
136+
id: generateID(),
137+
field: 'groupedField1',
138+
operator: '=',
139+
value: 'groupedField4',
140+
valueSource: 'field',
141+
},
142+
{
143+
id: generateID(),
144+
field: 'birthdate',
145+
operator: 'between',
146+
value: '1954-10-03,1960-06-06',
147+
},
148+
],
149+
};
150+
151+
export const initialQueryIC: RuleGroupTypeIC = convertToIC(initialQuery);

‎dev/main.tsx

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { ChakraProvider, extendTheme } from '@chakra-ui/react';
2+
import * as React from 'react';
3+
import { createRoot } from 'react-dom/client';
4+
import { QueryBuilder } from 'react-querybuilder';
5+
import { QueryBuilderChakra } from '../src';
6+
import './styles.scss';
7+
import { fields, initialQuery } from './constants';
8+
9+
const chakraTheme = extendTheme({
10+
components: {
11+
Button: {
12+
baseStyle: {
13+
color: 'rebeccapurple',
14+
fontWeight: 'bold', // Normally "semibold"
15+
},
16+
},
17+
},
18+
config: {
19+
initialColorMode: 'light',
20+
useSystemColorMode: false,
21+
},
22+
});
23+
24+
const App = () => {
25+
return (
26+
<div id="app">
27+
<ChakraProvider theme={chakraTheme}>
28+
<QueryBuilderChakra>
29+
<QueryBuilder fields={fields} defaultQuery={initialQuery} />
30+
</QueryBuilderChakra>
31+
</ChakraProvider>
32+
</div>
33+
);
34+
};
35+
36+
createRoot(document.querySelector('#app')!).render(
37+
<React.StrictMode>
38+
<App />
39+
</React.StrictMode>
40+
);

‎dev/musicalInstruments.ts

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
// Adapted from https://en.wikipedia.org/wiki/List_of_musical_instruments
2+
import type { OptionGroup } from 'react-querybuilder';
3+
4+
export const musicalInstruments: OptionGroup[] = [
5+
{
6+
label: 'Percussion instruments',
7+
instruments: [
8+
'Clapstick',
9+
'Cowbell',
10+
'Cymbal',
11+
'Gong',
12+
'Maraca',
13+
'Marimba',
14+
'More cowbell',
15+
'Spoon',
16+
'Steelpan',
17+
'Tambourine',
18+
'Triangle',
19+
'Vibraphone',
20+
'Washboard',
21+
'Wood block',
22+
'Wooden fish',
23+
'Xylophone',
24+
],
25+
},
26+
{
27+
label: 'Membranophones',
28+
instruments: [
29+
'Barrel drum',
30+
'Bass drum',
31+
'Bongo drums',
32+
'Conga',
33+
'Drum',
34+
'Drum kit',
35+
"Jew's harp",
36+
'Octaban',
37+
'Samphor',
38+
'Snare drum',
39+
'Timpani',
40+
'Tom-tom',
41+
],
42+
},
43+
{
44+
label: 'Wind instruments',
45+
instruments: [
46+
'Accordion',
47+
'Air horn',
48+
'Bagpipe',
49+
'Baritone horn',
50+
'Bassoon',
51+
'Bazooka',
52+
'Beatboxing',
53+
'Blown bottle',
54+
'Bugle',
55+
'Clarinet',
56+
'Conch',
57+
'Cornet',
58+
'Didgeridoo',
59+
'Double bell euphonium',
60+
'Doulophone',
61+
'English horn',
62+
'Euphonium',
63+
'Flugelhorn',
64+
'Flute',
65+
'French horn',
66+
'Harmonica',
67+
'Irish flute',
68+
'Jug',
69+
'Kazoo',
70+
'Melodeon',
71+
'Mezzo-soprano',
72+
'Oboe',
73+
'Ocarina',
74+
'Pan flute',
75+
'Piccolo',
76+
'Pipe organ',
77+
'Recorder',
78+
'Saxophone',
79+
'Slide whistle',
80+
'Sousaphone',
81+
'Trombone',
82+
'Trumpet',
83+
'Tuba',
84+
'Whistle',
85+
],
86+
},
87+
{
88+
label: 'Stringed instruments',
89+
instruments: [
90+
'Aeolian harp',
91+
'Bandolin',
92+
'Banjo ukulele',
93+
'Cello',
94+
'Chapman stick',
95+
'Clavichord',
96+
'Clavinet',
97+
'Double bass',
98+
'Dulcimer',
99+
'Fiddle',
100+
'Guitar',
101+
'Hammered dulcimer',
102+
'Harp',
103+
'Harpsichord',
104+
'Lute',
105+
'Lyre',
106+
'Maguhu',
107+
'Mandola',
108+
'Mandolin',
109+
'Octobass',
110+
'Piano',
111+
'Sitar',
112+
'Ukulele',
113+
'Viol',
114+
'Violin',
115+
'Washtub bass',
116+
],
117+
},
118+
{
119+
label: 'Electronic instruments',
120+
instruments: [
121+
'AlphaSphere',
122+
'Audiocubes',
123+
'Bass pedals',
124+
'Continuum Fingerboard',
125+
'Croix Sonore',
126+
"Denis d'or",
127+
'Dubreq stylophone',
128+
'Drum machine',
129+
'Eigenharp',
130+
'Electric guitar',
131+
'Electronic keyboard',
132+
'Electronic organ',
133+
'EWI',
134+
'Fingerboard synthesizer',
135+
'Hammond organ',
136+
'Keyboard',
137+
'Keytar',
138+
'Kraakdoos',
139+
'Laser harp',
140+
'Mellotron',
141+
'MIDI keyboard',
142+
'Omnichord',
143+
'Ondes Martenot',
144+
'Otamatone',
145+
'Sampler',
146+
'Seaboard music instrument',
147+
'Skoog',
148+
'Synclavier',
149+
'Synthesizer',
150+
'Teleharmonium',
151+
'Tenori-on',
152+
'Theremin',
153+
'trautonium',
154+
'Turntablism',
155+
'Turntable',
156+
],
157+
},
158+
].map(({ label, instruments }) => ({
159+
label,
160+
options: instruments.map(s => ({ name: s, label: s })),
161+
}));

‎dev/styles.scss

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
@use 'react-querybuilder/dist/query-builder.scss';
2+
3+
#app {
4+
padding: 0.5rem;
5+
}
6+
7+
.chakra-select__wrapper {
8+
width: fit-content !important;
9+
display: inline-block;
10+
}
11+
12+
.chakra-input {
13+
width: auto !important;
14+
display: inline-block;
15+
}
16+
17+
.chakra-radio-group {
18+
display: inline-block;
19+
}

‎example/.codesandbox/tasks.json

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"setupTasks": [
3+
{
4+
"name": "Install Bun",
5+
"command": "npm install --global bun"
6+
},
7+
{
8+
"name": "Install Dependencies",
9+
"command": "bun install"
10+
}
11+
],
12+
"tasks": {
13+
"build": {
14+
"name": "build",
15+
"command": "bun run build"
16+
},
17+
"dev": {
18+
"name": "dev",
19+
"command": "bun run dev",
20+
"runAtStart": true,
21+
"preview": {
22+
"port": 5173
23+
}
24+
},
25+
"preview": {
26+
"name": "preview",
27+
"command": "bun run preview"
28+
}
29+
}
30+
}

‎example/.codesandbox/template.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"description": "React Query Builder Chakra UI v2 Template",
3+
"iconUrl": "https://react-querybuilder.js.org/img/react-querybuilder-64.png",
4+
"published": true,
5+
"tags": [
6+
"react",
7+
"query",
8+
"builder",
9+
"operators",
10+
"ui",
11+
"component",
12+
"clause",
13+
"expression",
14+
"sql"
15+
],
16+
"title": "React Query Builder Chakra UI v2 Template"
17+
}

‎example/.codesandbox/workspace.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"responsive-preview": {
3+
"Mobile": [320, 675],
4+
"Tablet": [1024, 765],
5+
"Desktop": [1400, 800],
6+
"Desktop HD": [1920, 1080]
7+
}
8+
}

‎example/.gitignore

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
pnpm-debug.log*
8+
lerna-debug.log*
9+
10+
node_modules
11+
dist
12+
dist-ssr
13+
*.local
14+
15+
# Editor directories and files
16+
.idea
17+
.DS_Store
18+
*.suo
19+
*.ntvs*
20+
*.njsproj
21+
*.sln
22+
*.sw?

‎example/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
## React Query Builder Chakra UI v2 Example
2+
3+
- [CodeSandbox](https://react-querybuilder.js.org/sandbox?t=chakra2) / [StackBlitz](https://react-querybuilder.js.org/sandbox?p=stackblitz&t=chakra2) example projects
4+
5+
[![Open in CodeSandbox](https://img.shields.io/badge/Open_in-CodeSandbox-000000?logo=codesandbox)](https://react-querybuilder.js.org/sandbox?t=chakra2) [![Open in StackBlitz](https://img.shields.io/badge/Open_in-StackBlitz-1269D3?logo=stackblitz)](https://react-querybuilder.js.org/sandbox?p=stackblitz&t=chakra2)
6+
7+
Click one of the links above to open this example as an online, editable demo, or clone it locally with [degit](https://www.npmjs.com/package/degit):
8+
9+
```bash
10+
npx degit github:react-querybuilder/react-querybuilder-chakra2/example rqb-example-chakra2
11+
```

‎example/index.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1" />
6+
<title>React Query Builder Chakra UI Example</title>
7+
</head>
8+
<body>
9+
<div id="root"></div>
10+
<script type="module" src="src/index.tsx"></script>
11+
</body>
12+
</html>

‎example/package.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"dependencies": {
3+
"@chakra-ui/icons": "^2",
4+
"@chakra-ui/react": "^2",
5+
"@chakra-ui/system": "^2",
6+
"@emotion/react": "^11",
7+
"@emotion/styled": "^11",
8+
"@react-querybuilder/chakra2": "7.7.1",
9+
"framer-motion": "^11",
10+
"react": "^18.3.1",
11+
"react-dom": "^18.3.1",
12+
"react-querybuilder": "7.7.1"
13+
},
14+
"description": "React Query Builder Chakra UI v2 Example",
15+
"devDependencies": {
16+
"@types/react": "^18",
17+
"@types/react-dom": "^18",
18+
"@vitejs/plugin-react": "^4.3.3",
19+
"sass": "^1.77.8",
20+
"typescript": "^5.5.4",
21+
"vite": "^5.3.5"
22+
},
23+
"name": "react-querybuilder-chakra2-example",
24+
"private": true,
25+
"scripts": {
26+
"build": "tsc && vite build",
27+
"dev": "vite",
28+
"preview": "vite preview"
29+
},
30+
"type": "module"
31+
}

‎example/src/App.tsx

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { ChakraProvider, extendTheme } from '@chakra-ui/react';
2+
import { QueryBuilderChakra } from '@react-querybuilder/chakra';
3+
import { useState } from 'react';
4+
import type { Field, RuleGroupType } from 'react-querybuilder';
5+
import { QueryBuilder, formatQuery } from 'react-querybuilder';
6+
import './styles.scss';
7+
8+
const chakraTheme = extendTheme({
9+
config: {
10+
initialColorMode: 'light',
11+
useSystemColorMode: false,
12+
},
13+
});
14+
15+
const fields: Field[] = [
16+
{ name: 'firstName', label: 'First Name' },
17+
{ name: 'lastName', label: 'Last Name' },
18+
];
19+
20+
const initialQuery: RuleGroupType = {
21+
combinator: 'and',
22+
rules: [
23+
{ field: 'firstName', operator: 'beginsWith', value: 'Stev' },
24+
{ field: 'lastName', operator: 'in', value: 'Vai,Vaughan' },
25+
],
26+
};
27+
28+
export const App = () => {
29+
const [query, setQuery] = useState(initialQuery);
30+
31+
return (
32+
<div>
33+
<ChakraProvider theme={chakraTheme}>
34+
<QueryBuilderChakra>
35+
<QueryBuilder
36+
fields={fields}
37+
query={query}
38+
onQueryChange={setQuery}
39+
/>
40+
</QueryBuilderChakra>
41+
</ChakraProvider>
42+
<h4>Query</h4>
43+
<pre>
44+
<code>{formatQuery(query, 'json')}</code>
45+
</pre>
46+
</div>
47+
);
48+
};

‎example/src/index.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { StrictMode } from 'react';
2+
import { createRoot } from 'react-dom/client';
3+
import { App } from './App';
4+
5+
createRoot(document.querySelector('#root')!).render(
6+
<StrictMode>
7+
<App />
8+
</StrictMode>
9+
);

‎example/src/styles.scss

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
@import 'react-querybuilder/dist/query-builder.scss';
2+
3+
body {
4+
margin: 0.5rem !important;
5+
}
6+
7+
code {
8+
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
9+
}
10+
11+
.queryBuilder {
12+
.chakra-select__wrapper {
13+
width: fit-content;
14+
display: inline-block;
15+
}
16+
.chakra-input {
17+
width: auto;
18+
display: inline-block;
19+
}
20+
.chakra-radio-group {
21+
display: inline-block;
22+
}
23+
}

‎example/src/vite-env.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/// <reference types="vite/client" />

‎example/tsconfig.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"include": ["src"],
3+
"exclude": ["node_modules"],
4+
"compilerOptions": {
5+
"strict": true,
6+
"esModuleInterop": false,
7+
"lib": ["DOM", "DOM.Iterable", "ESNext"],
8+
"jsx": "react-jsx",
9+
"target": "ESNext",
10+
"useDefineForClassFields": true,
11+
"skipLibCheck": true,
12+
"allowSyntheticDefaultImports": true,
13+
"forceConsistentCasingInFileNames": true,
14+
"module": "ESNext",
15+
"moduleResolution": "Bundler",
16+
"resolveJsonModule": true,
17+
"isolatedModules": true,
18+
"noEmit": true
19+
}
20+
}

‎example/vite.config.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import react from '@vitejs/plugin-react';
2+
import { defineConfig } from 'vite';
3+
4+
export default defineConfig({
5+
plugins: [react()],
6+
server: {
7+
port: 5173,
8+
},
9+
});

‎index.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>React Query Builder Dev Page (Chakra UI)</title>
7+
</head>
8+
<body>
9+
<div id="app"></div>
10+
<script type="module" src="./dev/main.tsx"></script>
11+
</body>
12+
</html>

‎package.json

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
{
2+
"name": "@react-querybuilder/chakra2",
3+
"description": "Custom Chakra UI v2 components for react-querybuilder",
4+
"version": "7.7.1",
5+
"publishConfig": {
6+
"access": "public"
7+
},
8+
"main": "./dist/cjs/index.js",
9+
"module": "./dist/react-querybuilder_chakra2.legacy-esm.js",
10+
"exports": {
11+
"./package.json": "./package.json",
12+
".": {
13+
"import": {
14+
"types": "./dist/types-esm/index.d.mts",
15+
"default": "./dist/react-querybuilder_chakra2.mjs"
16+
},
17+
"require": {
18+
"types": "./dist/types/index.d.ts",
19+
"default": "./dist/cjs/index.js"
20+
}
21+
}
22+
},
23+
"types": "./dist/types/index.d.ts",
24+
"files": [
25+
"dist"
26+
],
27+
"sideEffects": false,
28+
"repository": {
29+
"type": "git",
30+
"url": "https://github.com/react-querybuilder/react-querybuilder-chakra2.git"
31+
},
32+
"license": "MIT",
33+
"homepage": "https://react-querybuilder.js.org/",
34+
"keywords": [
35+
"react",
36+
"querybuilder",
37+
"chakra",
38+
"chakra-ui",
39+
"query",
40+
"builder",
41+
"operators",
42+
"component",
43+
"clause",
44+
"expression",
45+
"sql"
46+
],
47+
"scripts": {
48+
"start": "bunx --bun vite",
49+
"build": "bunx --bun tsup",
50+
"typecheck": "tsc --noEmit",
51+
"typecheck:watch": "tsc --noEmit --watch",
52+
"pretty-print": "prettier --config prettier.config.mjs --write '*.*' './src/**' './dev/**' './example/**'",
53+
"pretty-check": "prettier --config prettier.config.mjs --check '*.*' './src/**' './dev/**' './example/**'",
54+
"are-the-types-wrong": "attw --format table-flipped --pack ."
55+
},
56+
"devDependencies": {
57+
"@arethetypeswrong/cli": "^0.16.4",
58+
"@chakra-ui/icons": "^2.2.4",
59+
"@chakra-ui/react": "^2.10.3",
60+
"@chakra-ui/system": "^2.6.2",
61+
"@emotion/react": "^11.13.3",
62+
"@emotion/styled": "^11.13.0",
63+
"@testing-library/dom": "^10.4.0",
64+
"@testing-library/jest-dom": "^6.6.3",
65+
"@testing-library/react": "^16.0.1",
66+
"@testing-library/user-event": "^14.5.2",
67+
"@types/bun": "^1.1.13",
68+
"@types/react": "^18.3.12",
69+
"@types/react-dom": "^18.3.1",
70+
"@types/web": "^0.0.176",
71+
"@vitejs/plugin-react": "^4.3.3",
72+
"framer-motion": "^11.11.11",
73+
"oxc-transform": "^0.35.0",
74+
"prettier": "3.3.3",
75+
"react": "^18.3.1",
76+
"react-dom": "^18.3.1",
77+
"react-querybuilder": "^7.7.1",
78+
"sass": "^1.80.6",
79+
"tsup": "^8.3.5",
80+
"typescript": "^5.6.3",
81+
"vite": "^5.4.10"
82+
},
83+
"peerDependencies": {
84+
"@chakra-ui/icons": "^2",
85+
"@chakra-ui/react": "^2",
86+
"@chakra-ui/system": "^2",
87+
"@emotion/react": "^11",
88+
"@emotion/styled": "^11",
89+
"framer-motion": "^11",
90+
"react": ">=18",
91+
"react-querybuilder": "^7.7.1"
92+
}
93+
}

‎prettier.config.mjs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/** @type {import("prettier").Options} */
2+
export default {
3+
printWidth: 100,
4+
tabWidth: 2,
5+
useTabs: false,
6+
semi: true,
7+
singleQuote: true,
8+
trailingComma: 'es5',
9+
bracketSpacing: true,
10+
bracketSameLine: true,
11+
arrowParens: 'avoid',
12+
overrides: [
13+
{
14+
files: 'example/**/*.!(css|scss)*',
15+
options: {
16+
printWidth: 80,
17+
},
18+
},
19+
],
20+
};

‎screenshot.png

27.2 KB
Loading

‎src/ChakraActionElement.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { Button } from '@chakra-ui/react';
2+
import type { ComponentPropsWithoutRef } from 'react';
3+
import * as React from 'react';
4+
import type { ActionWithRulesProps } from 'react-querybuilder';
5+
6+
export type ChakraActionProps = ActionWithRulesProps & ComponentPropsWithoutRef<typeof Button>;
7+
8+
export const ChakraActionElement = ({
9+
className,
10+
handleOnClick,
11+
label,
12+
title,
13+
disabled,
14+
disabledTranslation,
15+
// Props that should not be in extraProps
16+
testID: _testID,
17+
rules: _rules,
18+
level: _level,
19+
path: _path,
20+
context: _context,
21+
validation: _validation,
22+
ruleOrGroup: _ruleOrGroup,
23+
schema: _schema,
24+
...extraProps
25+
}: ChakraActionProps): React.JSX.Element => (
26+
<Button
27+
className={className}
28+
title={disabledTranslation && disabled ? disabledTranslation.title : title}
29+
onClick={e => handleOnClick(e)}
30+
isDisabled={disabled && !disabledTranslation}
31+
{...extraProps}>
32+
{disabledTranslation && disabled ? disabledTranslation.label : label}
33+
</Button>
34+
);

‎src/ChakraDragHandle.tsx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { DragHandleIcon } from '@chakra-ui/icons';
2+
import { IconButton } from '@chakra-ui/react';
3+
import type { ComponentPropsWithRef } from 'react';
4+
import * as React from 'react';
5+
import { forwardRef } from 'react';
6+
import type { DragHandleProps } from 'react-querybuilder';
7+
8+
type IBP = ComponentPropsWithRef<typeof IconButton>;
9+
10+
export type ChakraDragHandleProps = DragHandleProps &
11+
Omit<IBP, 'aria-label'> &
12+
Partial<Pick<IBP, 'aria-label'>>;
13+
14+
export const ChakraDragHandle: React.ForwardRefExoticComponent<
15+
Omit<ChakraDragHandleProps, 'ref'> & React.RefAttributes<HTMLSpanElement>
16+
> = forwardRef<HTMLSpanElement, ChakraDragHandleProps>(
17+
(
18+
{
19+
className,
20+
title,
21+
disabled,
22+
// Props that should not be in extraProps
23+
testID: _testID,
24+
level: _level,
25+
path: _path,
26+
label: _label,
27+
context: _context,
28+
validation: _validation,
29+
schema: _schema,
30+
ruleOrGroup: _ruleOrGroup,
31+
...extraProps
32+
},
33+
dragRef
34+
) => (
35+
<span ref={dragRef} className={className} title={title}>
36+
<IconButton
37+
isDisabled={disabled}
38+
icon={<DragHandleIcon />}
39+
aria-label={title ?? /* istanbul ignore next */ ''}
40+
{...extraProps}
41+
/>
42+
</span>
43+
)
44+
);

‎src/ChakraNotToggle.tsx

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { FormControl, FormLabel, Switch } from '@chakra-ui/react';
2+
import type { ComponentPropsWithoutRef } from 'react';
3+
import * as React from 'react';
4+
import { useId } from 'react';
5+
import type { NotToggleProps } from 'react-querybuilder';
6+
7+
export type ChakraNotToggleProps = NotToggleProps & ComponentPropsWithoutRef<typeof Switch>;
8+
9+
export const ChakraNotToggle = ({
10+
className,
11+
handleOnChange,
12+
label,
13+
checked,
14+
title,
15+
disabled,
16+
// Props that should not be in extraProps
17+
path: _path,
18+
context: _context,
19+
validation: _validation,
20+
testID: _testID,
21+
schema: _schema,
22+
ruleGroup: _ruleGroup,
23+
...extraProps
24+
}: ChakraNotToggleProps): React.JSX.Element => {
25+
const id = useId();
26+
27+
return (
28+
<div style={{ display: 'inline-block' }}>
29+
<FormControl
30+
display="flex"
31+
alignItems="center"
32+
className={className}
33+
title={title}
34+
isDisabled={disabled}>
35+
<Switch
36+
id={id}
37+
isChecked={checked}
38+
isDisabled={disabled}
39+
onChange={e => handleOnChange(e.target.checked)}
40+
{...extraProps}
41+
/>
42+
<FormLabel htmlFor={id} marginBottom={0}>
43+
{label}
44+
</FormLabel>
45+
</FormControl>
46+
</div>
47+
);
48+
};

‎src/ChakraShiftActions.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Button } from '@chakra-ui/react';
2+
import * as React from 'react';
3+
import type { ShiftActionsProps } from 'react-querybuilder';
4+
5+
export const ChakraShiftActions = ({
6+
shiftUp,
7+
shiftDown,
8+
shiftUpDisabled,
9+
shiftDownDisabled,
10+
disabled,
11+
className,
12+
labels,
13+
titles,
14+
testID,
15+
}: ShiftActionsProps): React.JSX.Element => (
16+
<div data-testid={testID} className={className}>
17+
<Button isDisabled={disabled || shiftUpDisabled} onClick={shiftUp} title={titles?.shiftUp}>
18+
{labels?.shiftUp}
19+
</Button>
20+
<Button
21+
isDisabled={disabled || shiftDownDisabled}
22+
onClick={shiftDown}
23+
title={titles?.shiftDown}>
24+
{labels?.shiftDown}
25+
</Button>
26+
</div>
27+
);

‎src/ChakraValueEditor.tsx

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
import { Checkbox, Input, Radio, RadioGroup, Stack, Switch, Textarea } from '@chakra-ui/react';
2+
import * as React from 'react';
3+
import type { ValueEditorProps } from 'react-querybuilder';
4+
import { ValueEditor, useValueEditor } from 'react-querybuilder';
5+
6+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
7+
type ChakraValueEditorProps = ValueEditorProps & { extraProps?: Record<string, any> };
8+
9+
export const ChakraValueEditor = (allProps: ChakraValueEditorProps): React.JSX.Element | null => {
10+
const {
11+
fieldData,
12+
operator,
13+
value,
14+
handleOnChange,
15+
title,
16+
className,
17+
type,
18+
inputType,
19+
values = [],
20+
listsAsArrays: _listsAsArrays,
21+
separator,
22+
valueSource: _vs,
23+
testID,
24+
disabled,
25+
selectorComponent: SelectorComponent = allProps.schema.controls.valueSelector,
26+
extraProps,
27+
parseNumbers: _parseNumbers,
28+
...propsForValueSelector
29+
} = allProps;
30+
31+
const { valueAsArray, multiValueHandler, valueListItemClassName } = useValueEditor(allProps);
32+
33+
if (operator === 'null' || operator === 'notNull') {
34+
return null;
35+
}
36+
37+
const placeHolderText = fieldData?.placeholder ?? '';
38+
const inputTypeCoerced = ['in', 'notIn'].includes(operator) ? 'text' : inputType || 'text';
39+
40+
if (
41+
(operator === 'between' || operator === 'notBetween') &&
42+
(type === 'select' || type === 'text')
43+
) {
44+
if (type === 'text') {
45+
const editors = ['from', 'to'].map((key, i) => {
46+
return (
47+
<Input
48+
key={key}
49+
type={inputTypeCoerced}
50+
value={valueAsArray[i] ?? ''}
51+
isDisabled={disabled}
52+
className={valueListItemClassName}
53+
placeholder={placeHolderText}
54+
onChange={e => multiValueHandler(e.target.value, i)}
55+
{...extraProps}
56+
/>
57+
);
58+
});
59+
return (
60+
<span data-testid={testID} className={className} title={title}>
61+
{editors[0]}
62+
{separator}
63+
{editors[1]}
64+
</span>
65+
);
66+
}
67+
return <ValueEditor {...allProps} skipHook />;
68+
}
69+
70+
switch (type) {
71+
case 'select':
72+
return (
73+
<SelectorComponent
74+
{...propsForValueSelector}
75+
className={className}
76+
title={title}
77+
value={value}
78+
disabled={disabled}
79+
handleOnChange={handleOnChange}
80+
options={values}
81+
/>
82+
);
83+
84+
case 'multiselect':
85+
return <ValueEditor {...allProps} skipHook />;
86+
87+
case 'textarea':
88+
return (
89+
<Textarea
90+
value={value}
91+
title={title}
92+
isDisabled={disabled}
93+
className={className}
94+
placeholder={placeHolderText}
95+
onChange={e => handleOnChange(e.target.value)}
96+
{...extraProps}
97+
/>
98+
);
99+
100+
case 'switch':
101+
return (
102+
<Switch
103+
className={className}
104+
isChecked={!!value}
105+
title={title}
106+
isDisabled={disabled}
107+
onChange={e => handleOnChange(e.target.checked)}
108+
{...extraProps}
109+
/>
110+
);
111+
112+
case 'checkbox':
113+
return (
114+
<Checkbox
115+
className={className}
116+
title={title}
117+
isDisabled={disabled}
118+
onChange={e => handleOnChange(e.target.checked)}
119+
isChecked={!!value}
120+
{...extraProps}
121+
/>
122+
);
123+
124+
case 'radio':
125+
return (
126+
<RadioGroup
127+
className={className}
128+
title={title}
129+
value={value}
130+
onChange={handleOnChange}
131+
isDisabled={disabled}
132+
{...extraProps}>
133+
<Stack direction="row">
134+
{values.map(v => (
135+
<Radio key={v.name} value={v.name}>
136+
{v.label}
137+
</Radio>
138+
))}
139+
</Stack>
140+
</RadioGroup>
141+
);
142+
}
143+
144+
return (
145+
<Input
146+
type={inputTypeCoerced}
147+
value={value}
148+
title={title}
149+
isDisabled={disabled}
150+
className={className}
151+
placeholder={placeHolderText}
152+
onChange={e => handleOnChange(e.target.value)}
153+
{...extraProps}
154+
/>
155+
);
156+
};

‎src/ChakraValueSelector.tsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { Select } from '@chakra-ui/react';
2+
import type { ComponentPropsWithoutRef } from 'react';
3+
import * as React from 'react';
4+
import type { VersatileSelectorProps } from 'react-querybuilder';
5+
import { toOptions } from 'react-querybuilder';
6+
7+
export type ChakraValueSelectorProps = VersatileSelectorProps &
8+
ComponentPropsWithoutRef<typeof Select>;
9+
10+
export const ChakraValueSelector = ({
11+
className,
12+
handleOnChange,
13+
options,
14+
value,
15+
title,
16+
disabled,
17+
// Props that should not be in extraProps
18+
testID: _testID,
19+
rule: _rule,
20+
rules: _rules,
21+
level: _level,
22+
path: _path,
23+
context: _context,
24+
validation: _validation,
25+
operator: _operator,
26+
field: _field,
27+
fieldData: _fieldData,
28+
multiple: _multiple,
29+
listsAsArrays: _listsAsArrays,
30+
schema: _schema,
31+
...extraProps
32+
}: ChakraValueSelectorProps): React.JSX.Element => (
33+
<Select
34+
className={className}
35+
title={title}
36+
value={value}
37+
disabled={disabled}
38+
onChange={e => handleOnChange(e.target.value)}
39+
{...extraProps}>
40+
{toOptions(options)}
41+
</Select>
42+
);

‎src/index.tsx

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import {
2+
ChevronDownIcon,
3+
ChevronUpIcon,
4+
CloseIcon,
5+
CopyIcon,
6+
LockIcon,
7+
UnlockIcon,
8+
} from '@chakra-ui/icons';
9+
import * as React from 'react';
10+
import type {
11+
ControlElementsProp,
12+
FullField,
13+
QueryBuilderContextProvider,
14+
Translations,
15+
} from 'react-querybuilder';
16+
import { getCompatContextProvider } from 'react-querybuilder';
17+
import { ChakraActionElement } from './ChakraActionElement';
18+
import { ChakraDragHandle } from './ChakraDragHandle';
19+
import { ChakraNotToggle } from './ChakraNotToggle';
20+
import { ChakraValueEditor } from './ChakraValueEditor';
21+
import { ChakraValueSelector } from './ChakraValueSelector';
22+
23+
export * from './ChakraActionElement';
24+
export * from './ChakraDragHandle';
25+
export * from './ChakraNotToggle';
26+
export * from './ChakraValueEditor';
27+
export * from './ChakraValueSelector';
28+
29+
export const chakraControlElements: ControlElementsProp<FullField, string> = {
30+
actionElement: ChakraActionElement,
31+
valueSelector: ChakraValueSelector,
32+
dragHandle: ChakraDragHandle,
33+
notToggle: ChakraNotToggle,
34+
valueEditor: ChakraValueEditor,
35+
};
36+
37+
export const chakraTranslations: Partial<Translations> = {
38+
removeGroup: { label: <CloseIcon /> },
39+
removeRule: { label: <CloseIcon /> },
40+
cloneRuleGroup: { label: <CopyIcon /> },
41+
cloneRule: { label: <CopyIcon /> },
42+
lockGroup: { label: <UnlockIcon /> },
43+
lockRule: { label: <UnlockIcon /> },
44+
lockGroupDisabled: { label: <LockIcon /> },
45+
lockRuleDisabled: { label: <LockIcon /> },
46+
shiftActionDown: { label: <ChevronDownIcon /> },
47+
shiftActionUp: { label: <ChevronUpIcon /> },
48+
};
49+
50+
export const QueryBuilderChakra: QueryBuilderContextProvider = getCompatContextProvider({
51+
controlElements: chakraControlElements,
52+
translations: chakraTranslations,
53+
});

‎tsconfig.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"compilerOptions": {
3+
"noEmit": true,
4+
"esModuleInterop": true,
5+
"declaration": true,
6+
"isolatedDeclarations": true,
7+
"isolatedModules": true,
8+
"jsx": "react",
9+
"module": "esnext",
10+
"moduleResolution": "node",
11+
"resolveJsonModule": true,
12+
"skipLibCheck": true,
13+
"strict": true,
14+
"target": "esnext",
15+
"types": ["@testing-library/jest-dom", "node", "web", "bun"]
16+
},
17+
"include": ["./src/", "./dev/"]
18+
}

‎tsup.config.ts

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import { writeFile } from 'node:fs/promises';
2+
import path from 'node:path';
3+
import { isolatedDeclaration } from 'oxc-transform';
4+
import type { Options } from 'tsup';
5+
import { defineConfig } from 'tsup';
6+
7+
const generateDTS = async (projDir: string): Promise<void> => {
8+
const g = new Bun.Glob('**/*.{ts,tsx}');
9+
10+
const files = g.scan(path.join(projDir, '/src/'));
11+
12+
let fileCount = 0;
13+
14+
for await (const filePath of files) {
15+
// Skip test and test utility files
16+
if (
17+
/(_internal|\.test|(t|T)estUtils)\.tsx?$/.test(filePath) ||
18+
filePath.startsWith('internal/')
19+
) {
20+
continue;
21+
}
22+
23+
const originalSource = await Bun.file(path.join(projDir, '/src/', filePath)).text();
24+
const { code } = isolatedDeclaration(filePath, originalSource);
25+
26+
// Write the CJS DTS file
27+
await Bun.write(path.join(projDir, '/dist/types/', filePath.replace(/\.tsx?$/, '.d.ts')), code);
28+
29+
// Convert DTS file to ESM-in-CJS-context
30+
const lines = code.split('\n');
31+
32+
for (const line in lines) {
33+
const eximLine = lines[line].match(/^(ex|im)port .* from "(\..*)";$/);
34+
if (eximLine) {
35+
const resolvedExImPath = path.join(projDir, '/src/', path.parse(filePath).dir, eximLine[2]);
36+
if (
37+
(await Bun.file(`${resolvedExImPath}.ts`).exists()) ||
38+
(await Bun.file(`${resolvedExImPath}.tsx`).exists())
39+
) {
40+
lines[line] = lines[line].replace(/";$/, `.mjs";`);
41+
} else if (
42+
(await Bun.file(`${resolvedExImPath}/index.ts`).exists()) ||
43+
(await Bun.file(`${resolvedExImPath}/index.tsx`).exists())
44+
) {
45+
lines[line] = lines[line].replace(/";$/, `/index.mjs";`);
46+
}
47+
}
48+
}
49+
await Bun.write(
50+
path.join(projDir, '/dist/types-esm/', filePath.replace(/\.tsx?$/, '.d.mts')),
51+
lines.join('\n')
52+
);
53+
54+
fileCount++;
55+
}
56+
console.log(`${fileCount} DTS files generated.`);
57+
};
58+
59+
const getCjsIndexWriter = (pkgName: string, debug?: boolean) => async (): Promise<void> => {
60+
await writeFile(
61+
`dist/cjs/${debug ? 'debug' : 'index'}.js`,
62+
`'use strict';
63+
if (process.env.NODE_ENV === 'production') {
64+
module.exports = require('./${pkgName}.cjs.production${debug ? '.debug' : ''}.js');
65+
} else {
66+
module.exports = require('./${pkgName}.cjs.development${debug ? '.debug' : ''}.js');
67+
}
68+
`
69+
);
70+
};
71+
72+
const tsupCommonConfig = (sourceDir: string) =>
73+
(async options => {
74+
const pkgName = `react-querybuilder${sourceDir.endsWith('react-querybuilder') ? '' : `_${sourceDir.split('/').pop()}`}`;
75+
const x = (await Bun.file(path.join(sourceDir + '/src/index.tsx')).exists()) ? 'x' : '';
76+
const entryPoint = `src/index.ts${x}`;
77+
78+
const commonOptions = {
79+
entry: { [pkgName]: entryPoint },
80+
sourcemap: true,
81+
esbuildPlugins: [],
82+
...options,
83+
} satisfies Options;
84+
85+
const productionOptions = {
86+
minify: true,
87+
replaceNodeEnv: true,
88+
} satisfies Options;
89+
90+
const opts: Options[] = [
91+
// ESM, standard bundler dev, embedded `process` references
92+
{
93+
...commonOptions,
94+
format: 'esm',
95+
clean: true,
96+
onSuccess: () => generateDTS(sourceDir),
97+
},
98+
// ESM, Webpack 4 support. Target ES2017 syntax to compile away optional chaining and spreads
99+
{
100+
...commonOptions,
101+
entry: { [`${pkgName}.legacy-esm`]: entryPoint },
102+
// ESBuild outputs `'.mjs'` by default for the 'esm' format. Force '.js'
103+
outExtension: () => ({ js: '.js' }),
104+
target: 'es2017',
105+
format: 'esm',
106+
},
107+
// ESM for use in browsers. Minified, with `process` compiled away
108+
{
109+
...commonOptions,
110+
...productionOptions,
111+
entry: { [`${pkgName}.production`]: entryPoint },
112+
format: 'esm',
113+
outExtension: () => ({ js: '.mjs' }),
114+
},
115+
// CJS development
116+
{
117+
...commonOptions,
118+
entry: { [`${pkgName}.cjs.development`]: entryPoint },
119+
format: 'cjs',
120+
outDir: './dist/cjs/',
121+
},
122+
// CJS production
123+
{
124+
...commonOptions,
125+
...productionOptions,
126+
entry: { [`${pkgName}.cjs.production`]: entryPoint },
127+
format: 'cjs',
128+
outDir: './dist/cjs/',
129+
onSuccess: getCjsIndexWriter(pkgName, false),
130+
},
131+
];
132+
133+
return opts;
134+
}) as (options: Options) => Promise<Options[]>;
135+
136+
export default defineConfig(tsupCommonConfig(import.meta.dir));

‎typedoc.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"entryPoints": ["src/index.tsx"],
3+
"extends": ["../../typedoc.json"]
4+
}

‎vite.config.mts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import vitePluginReact from '@vitejs/plugin-react';
2+
import { defineConfig } from 'vite';
3+
4+
export default defineConfig({
5+
plugins: [vitePluginReact()],
6+
server: { port: 3104 },
7+
});

0 commit comments

Comments
 (0)
Please sign in to comment.