Stencil Component Starter
npm install query-builder-component
> A fully customizable, feature-rich MongoDB query builder web component built with Stencil.js. Create complex MongoDB queries through an intuitive UI with complete styling control via CSS parts and content customization through slots.
- Visual Query Building: Build MongoDB queries through an intuitive UI
- Field & Where Conditions: Support for field-based conditions and $where expressions
- Multiple Operators: Full support for MongoDB operators ($eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $exists, $regex, $size, $type)
- AND/OR Logic: Combine conditions with AND/OR logic
- Undo/Redo: Full history support with undo/redo functionality
- JSON Input Support: Automatically extract fields from JSON data
- Fully Customizable: 60+ CSS parts and 15+ slots for complete customization
- Shadow DOM: Encapsulated styles with full customization support
- TypeScript: Built with TypeScript for type safety
- Framework Agnostic: Works with any framework or vanilla JavaScript
bash
npm install query-builder-component
`$3
`html
`šÆ Quick Start
$3
`html
`$3
`jsx
import React, { useRef, useEffect } from 'react';
import 'query-builder-component';function App() {
const queryBuilderRef = useRef(null);
useEffect(() => {
const element = queryBuilderRef.current;
element.jsonInput = [{ / your data / }];
element.addEventListener('queryChanged', (event) => {
console.log('Query changed:', event.detail);
});
}, []);
return (
);
}
`šØ Customization Guide
The Query Builder Component offers extensive customization through CSS parts and slots, allowing you to completely transform its appearance and behavior without modifying the source code.
$3
The component exposes 60+ CSS parts for styling. Here's a complete reference:
#### Container Parts
`css
/ Main containers /
query-builder-component::part(root-container) { / Root wrapper / }
query-builder-component::part(main-panel) { / Main panel / }
query-builder-component::part(content-wrapper) { / Content area / }
query-builder-component::part(query-builder-container) { / Query builder container / }
`#### Header Parts
`css
/ Header section /
query-builder-component::part(header-section) { / Header container / }
query-builder-component::part(header-title) { / Header title / }
query-builder-component::part(header-description) { / Header description / }
`#### Toolbar Parts
`css
/ Toolbar /
query-builder-component::part(toolbar) { / Toolbar container / }
query-builder-component::part(toolbar-left) { / Left side of toolbar / }
query-builder-component::part(toolbar-title) { / Toolbar title / }
query-builder-component::part(history-controls) { / Undo/redo container / }
query-builder-component::part(undo-button) { / Undo button / }
query-builder-component::part(redo-button) { / Redo button / }
query-builder-component::part(undo-icon-svg) { / Undo icon SVG / }
query-builder-component::part(redo-icon-svg) { / Redo icon SVG / }
`#### Conditions Display Parts
`css
/ Conditions list /
query-builder-component::part(conditions-container) { / Conditions container / }
query-builder-component::part(conditions-header) { / Conditions header / }
query-builder-component::part(conditions-title) { / Conditions title / }
query-builder-component::part(clear-all-button) { / Clear all button / }
query-builder-component::part(conditions-list) { / Conditions list / }/ Individual condition /
query-builder-component::part(condition-row) { / Condition row / }
query-builder-component::part(condition-content) { / Condition content / }
query-builder-component::part(condition-group-badge) { / AND/OR badge / }
query-builder-component::part(condition-where-badge) { / WHERE badge / }
query-builder-component::part(condition-field) { / Field name / }
query-builder-component::part(condition-operator) { / Operator / }
query-builder-component::part(condition-value) { / Value / }
query-builder-component::part(condition-actions) { / Actions container / }
query-builder-component::part(edit-button) { / Edit button / }
query-builder-component::part(delete-button) { / Delete button / }
query-builder-component::part(edit-icon-svg) { / Edit icon SVG / }
query-builder-component::part(delete-icon-svg) { / Delete icon SVG / }
`#### Add Condition Form Parts
`css
/ Add condition form /
query-builder-component::part(add-condition-container) { / Form container / }
query-builder-component::part(add-condition-header) { / Form header / }
query-builder-component::part(add-condition-title) { / Form title / }
query-builder-component::part(close-add-button) { / Close button / }
query-builder-component::part(close-icon-svg) { / Close icon SVG / }/ Condition type selector /
query-builder-component::part(condition-type-selector) { / Type selector container / }
query-builder-component::part(condition-type-label) { / Type label / }
query-builder-component::part(condition-type-buttons) { / Type buttons container / }
query-builder-component::part(field-condition-button) { / Field condition button / }
query-builder-component::part(where-expression-button) { / Where expression button / }
/ Input fields /
query-builder-component::part(field-selector) { / Field selector container / }
query-builder-component::part(field-label) { / Field label / }
query-builder-component::part(field-dropdown-container) { / Field dropdown container / }
query-builder-component::part(field-select) { / Field select element / }
query-builder-component::part(operator-selector) { / Operator selector container / }
query-builder-component::part(operator-label) { / Operator label / }
query-builder-component::part(operator-dropdown-container) { / Operator dropdown container / }
query-builder-component::part(operator-select) { / Operator select element / }
query-builder-component::part(value-input-container) { / Value input container / }
query-builder-component::part(value-label) { / Value label / }
query-builder-component::part(value-input) { / Value input field / }
query-builder-component::part(value-hint) { / Value hint text / }
query-builder-component::part(where-input-container) { / Where input container / }
query-builder-component::part(where-label) { / Where label / }
query-builder-component::part(where-input) { / Where input field / }
query-builder-component::part(where-hint) { / Where hint text / }
/ Action buttons /
query-builder-component::part(action-buttons) { / Action buttons container / }
query-builder-component::part(add-and-button) { / Add to AND button / }
query-builder-component::part(add-or-button) { / Add to OR button / }
`#### Query Output Parts
`css
/ Query output /
query-builder-component::part(query-output) { / Output container / }
query-builder-component::part(query-output-title) { / Output title / }
query-builder-component::part(query-output-content) { / Output content / }
`$3
Slots allow you to replace content and icons with your own HTML:
#### Content Slots
`html
Custom Header
Custom description
ā New Condition
Clear All
Choose Type:
Field Based
Custom Expression
Select Field:
Select Operator:
Enter Value:
Tip: Use JSON format
Enter Expression:
Use "this" to refer to the document
ADD WITH AND
ADD WITH OR
Generated Query:
`#### Icon Slots
`html
`š Theme Examples
$3
`css
/ Dark theme styling /
query-builder-component::part(root-container) {
background: #1f2937;
color: #f3f4f6;
}query-builder-component::part(main-panel) {
background: #111827;
border: 1px solid #374151;
}
query-builder-component::part(header-section) {
background: #1f2937;
border-bottom: 1px solid #374151;
}
query-builder-component::part(header-title) {
color: #f9fafb;
}
query-builder-component::part(condition-row) {
background: #111827;
border-color: #374151;
}
query-builder-component::part(condition-field) {
color: #60a5fa;
}
query-builder-component::part(condition-operator) {
color: #34d399;
}
query-builder-component::part(condition-value) {
color: #c084fc;
}
query-builder-component::part(query-output) {
background: #030712;
color: #f3f4f6;
}
`$3
`css
/ Material Design inspired theme /
query-builder-component::part(main-panel) {
box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
border: none;
}query-builder-component::part(header-section) {
background: #6200ea;
padding: 1.5rem;
color: white;
}
query-builder-component::part(undo-button),
query-builder-component::part(redo-button) {
border-radius: 50%;
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.3s;
}
query-builder-component::part(undo-button):hover {
background: rgba(98, 0, 234, 0.08);
}
query-builder-component::part(add-and-button),
query-builder-component::part(add-or-button) {
text-transform: uppercase;
letter-spacing: 1px;
font-weight: 500;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
`$3
`css
/ Beautiful gradient theme /
query-builder-component::part(header-section) {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 1.5rem;
}query-builder-component::part(toolbar) {
background: linear-gradient(90deg, #f093fb 0%, #f5576c 100%);
border-radius: 8px;
}
query-builder-component::part(add-and-button) {
background: linear-gradient(45deg, #2196F3 30%, #21CBF3 90%);
box-shadow: 0 3px 5px 2px rgba(33, 203, 243, .3);
}
query-builder-component::part(add-or-button) {
background: linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%);
box-shadow: 0 3px 5px 2px rgba(255, 105, 135, .3);
}
`š API Reference
$3
| Property | Type | Description | Default |
|----------|------|-------------|---------|
|
value | string | The current MongoDB query as JSON string | '{}' |
| availableFields | string[] | Array of field names available for querying | [] |
| jsonInput | object \| any[] | JSON data to extract fields from | null |
| renderCondition | (condition: QueryCondition) => string | Custom render function for conditions | undefined |
| localization | object | Localization object for text and operators | undefined |$3
| Event | Detail | Description |
|-------|--------|-------------|
|
queryChanged | string | Emitted when the query changes |
| historyUpdated | { canUndo: boolean; canRedo: boolean } | Emitted when history state changes |$3
| Method | Returns | Description |
|--------|---------|-------------|
|
undo() | Promise | Undo the last query change |
| redo() | Promise | Redo the previously undone change |$3
`javascript
const localization = {
operators: {
'$eq': 'equals',
'$ne': 'not equals',
'$gt': 'greater than',
'$gte': 'greater or equal',
'$lt': 'less than',
'$lte': 'less or equal',
'$in': 'in array',
'$nin': 'not in array',
'$exists': 'exists',
'$regex': 'matches pattern',
'$size': 'array size',
'$type': 'type is'
},
addForm: {
title: 'Add New Condition',
// ... other form labels
}
};queryBuilder.localization = localization;
`š§ Advanced Usage
$3
You can provide a custom render function to change how conditions are displayed:
`javascript
queryBuilder.renderCondition = (condition) => {
if (condition.type === 'field') {
return ;
} else {
return ${condition.expression};
}
};
`$3
`javascript
// Set a query programmatically
queryBuilder.value = JSON.stringify({
"$and": [
{ "age": { "$gte": 25 } },
{ "department": { "$eq": "Engineering" } }
]
}, null, 2);
`$3
#### Vue 3
`vue
ref="queryBuilder"
@queryChanged="handleQueryChange"
>
`#### Angular
`typescript
import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
import 'query-builder-component';@Component({
selector: 'app-root',
template:
})
export class AppComponent implements AfterViewInit {
@ViewChild('queryBuilder') queryBuilder: ElementRef;
ngAfterViewInit() {
const element = this.queryBuilder.nativeElement;
element.jsonInput = [/ your data /];
element.addEventListener('queryChanged', (event: CustomEvent) => {
console.log('Query:', event.detail);
});
}
}
`š Troubleshooting
$3
1. Component not rendering: Ensure the script is loaded as a module:
`html
`2. Styles not applying: Remember that the component uses Shadow DOM. Use
::part() selectors:
`css
/ Wrong /
query-builder-component .header { }
/ Correct /
query-builder-component::part(header-section) { }
`3. Slots not working: Ensure slot content is direct children of the component:
`html
Header
Header
`š¤ Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
1. Fork the repository
2. Create your feature branch (
git checkout -b feature/AmazingFeature)
3. Commit your changes (git commit -m 'Add some AmazingFeature')
4. Push to the branch (git push origin feature/AmazingFeature`)MIT License - see the LICENSE file for details.
- Built with Stencil.js
- Inspired by MongoDB's query language
- Icons from Lucide
For support, please open an issue in the GitHub repository.