@shotleybuilder/svelte-gridlite-kit 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +260 -0
  2. package/dist/GridLite.svelte +1361 -0
  3. package/dist/GridLite.svelte.d.ts +42 -0
  4. package/dist/components/CellContextMenu.svelte +209 -0
  5. package/dist/components/CellContextMenu.svelte.d.ts +28 -0
  6. package/dist/components/ColumnMenu.svelte +234 -0
  7. package/dist/components/ColumnMenu.svelte.d.ts +29 -0
  8. package/dist/components/ColumnPicker.svelte +403 -0
  9. package/dist/components/ColumnPicker.svelte.d.ts +29 -0
  10. package/dist/components/FilterBar.svelte +390 -0
  11. package/dist/components/FilterBar.svelte.d.ts +38 -0
  12. package/dist/components/FilterCondition.svelte +643 -0
  13. package/dist/components/FilterCondition.svelte.d.ts +35 -0
  14. package/dist/components/GroupBar.svelte +463 -0
  15. package/dist/components/GroupBar.svelte.d.ts +33 -0
  16. package/dist/components/RowDetailModal.svelte +213 -0
  17. package/dist/components/RowDetailModal.svelte.d.ts +25 -0
  18. package/dist/components/SortBar.svelte +232 -0
  19. package/dist/components/SortBar.svelte.d.ts +30 -0
  20. package/dist/components/SortCondition.svelte +129 -0
  21. package/dist/components/SortCondition.svelte.d.ts +30 -0
  22. package/dist/index.d.ts +23 -0
  23. package/dist/index.js +29 -0
  24. package/dist/query/builder.d.ts +160 -0
  25. package/dist/query/builder.js +432 -0
  26. package/dist/query/live.d.ts +50 -0
  27. package/dist/query/live.js +118 -0
  28. package/dist/query/schema.d.ts +30 -0
  29. package/dist/query/schema.js +75 -0
  30. package/dist/state/migrations.d.ts +29 -0
  31. package/dist/state/migrations.js +113 -0
  32. package/dist/state/views.d.ts +54 -0
  33. package/dist/state/views.js +130 -0
  34. package/dist/styles/gridlite.css +966 -0
  35. package/dist/types.d.ts +164 -0
  36. package/dist/types.js +2 -0
  37. package/dist/utils/filters.d.ts +14 -0
  38. package/dist/utils/filters.js +49 -0
  39. package/dist/utils/formatters.d.ts +16 -0
  40. package/dist/utils/formatters.js +39 -0
  41. package/dist/utils/fuzzy.d.ts +47 -0
  42. package/dist/utils/fuzzy.js +142 -0
  43. package/package.json +76 -0
@@ -0,0 +1,25 @@
1
+ import { SvelteComponent } from "svelte";
2
+ declare const __propDef: {
3
+ props: {
4
+ isOpen?: boolean;
5
+ hasPrev?: boolean;
6
+ hasNext?: boolean;
7
+ onClose: () => void;
8
+ onPrev?: (() => void) | undefined;
9
+ onNext?: (() => void) | undefined;
10
+ };
11
+ events: {
12
+ [evt: string]: CustomEvent<any>;
13
+ };
14
+ slots: {
15
+ default: {};
16
+ };
17
+ exports?: {} | undefined;
18
+ bindings?: string | undefined;
19
+ };
20
+ export type RowDetailModalProps = typeof __propDef.props;
21
+ export type RowDetailModalEvents = typeof __propDef.events;
22
+ export type RowDetailModalSlots = typeof __propDef.slots;
23
+ export default class RowDetailModal extends SvelteComponent<RowDetailModalProps, RowDetailModalEvents, RowDetailModalSlots> {
24
+ }
25
+ export {};
@@ -0,0 +1,232 @@
1
+ <script>import SortConditionComponent from "./SortCondition.svelte";
2
+ export let columns;
3
+ export let columnConfigs = [];
4
+ export let sorting = [];
5
+ export let onSortingChange;
6
+ export let isExpanded = false;
7
+ export let onExpandedChange = void 0;
8
+ function setExpanded(value) {
9
+ isExpanded = value;
10
+ if (onExpandedChange) {
11
+ onExpandedChange(value);
12
+ }
13
+ }
14
+ function addSort() {
15
+ const newSort = { column: "", direction: "asc" };
16
+ onSortingChange([...sorting, newSort]);
17
+ setExpanded(true);
18
+ }
19
+ function updateSort(index, column, direction) {
20
+ const newSorting = [...sorting];
21
+ newSorting[index] = { column, direction };
22
+ onSortingChange(newSorting);
23
+ }
24
+ function removeSort(index) {
25
+ const newSorting = sorting.filter((_, i) => i !== index);
26
+ onSortingChange(newSorting);
27
+ if (newSorting.length === 0) {
28
+ setExpanded(false);
29
+ }
30
+ }
31
+ function clearAllSorts() {
32
+ onSortingChange([]);
33
+ setExpanded(false);
34
+ }
35
+ $: availableColumnsForNew = columns.filter(
36
+ (col) => !sorting.some((s) => s.column === col.name)
37
+ );
38
+ $: hasSorts = sorting.length > 0;
39
+ $: validSortCount = sorting.filter((s) => s.column !== "").length;
40
+ $: canAddMore = availableColumnsForNew.length > 0;
41
+ </script>
42
+
43
+ <div class="sort-bar">
44
+ <button class="sort-toggle-btn" on:click={() => setExpanded(!isExpanded)}>
45
+ <svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
46
+ <path
47
+ stroke-linecap="round"
48
+ stroke-linejoin="round"
49
+ stroke-width="2"
50
+ d="M3 4h13M3 8h9m-9 4h6m4 0l4-4m0 0l4 4m-4-4v12"
51
+ />
52
+ </svg>
53
+ Sort
54
+ {#if validSortCount > 0}
55
+ <span class="sort-badge">{validSortCount}</span>
56
+ {/if}
57
+ <svg
58
+ class="chevron"
59
+ class:expanded={isExpanded}
60
+ fill="none"
61
+ stroke="currentColor"
62
+ viewBox="0 0 24 24"
63
+ >
64
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
65
+ </svg>
66
+ </button>
67
+
68
+ {#if isExpanded}
69
+ <div class="sort-panel">
70
+ {#if hasSorts}
71
+ <div class="sort-header">
72
+ <span class="sort-label">Sort by</span>
73
+ <button class="clear-all-btn" on:click={clearAllSorts}> Clear all </button>
74
+ </div>
75
+
76
+ <div class="sort-levels">
77
+ {#each sorting as sort, index (index)}
78
+ <SortConditionComponent
79
+ {sort}
80
+ {columns}
81
+ {columnConfigs}
82
+ existingSorts={sorting}
83
+ onUpdate={(column, direction) => updateSort(index, column, direction)}
84
+ onRemove={() => removeSort(index)}
85
+ />
86
+ {/each}
87
+ </div>
88
+ {/if}
89
+
90
+ <button class="add-sort-btn" on:click={addSort} disabled={!canAddMore}>
91
+ <svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
92
+ <path
93
+ stroke-linecap="round"
94
+ stroke-linejoin="round"
95
+ stroke-width="2"
96
+ d="M12 6v6m0 0v6m0-6h6m-6 0H6"
97
+ />
98
+ </svg>
99
+ {hasSorts ? 'Add another sort' : 'Add a sort'}
100
+ </button>
101
+ </div>
102
+ {/if}
103
+ </div>
104
+
105
+ <style>
106
+ .sort-bar {
107
+ position: relative;
108
+ }
109
+
110
+ .sort-toggle-btn {
111
+ display: inline-flex;
112
+ align-items: center;
113
+ gap: 0.5rem;
114
+ padding: 0.5rem 1rem;
115
+ font-size: 0.875rem;
116
+ font-weight: 500;
117
+ color: #374151;
118
+ background: white;
119
+ border: 1px solid #d1d5db;
120
+ border-radius: 0.375rem;
121
+ cursor: pointer;
122
+ transition: all 0.2s;
123
+ }
124
+
125
+ .sort-toggle-btn:hover {
126
+ background: #f9fafb;
127
+ border-color: #9ca3af;
128
+ }
129
+
130
+ .sort-badge {
131
+ display: inline-flex;
132
+ align-items: center;
133
+ justify-content: center;
134
+ min-width: 1.25rem;
135
+ height: 1.25rem;
136
+ padding: 0 0.375rem;
137
+ font-size: 0.75rem;
138
+ font-weight: 600;
139
+ color: white;
140
+ background: #f59e0b;
141
+ border-radius: 0.75rem;
142
+ }
143
+
144
+ .chevron {
145
+ width: 1rem;
146
+ height: 1rem;
147
+ transition: transform 0.2s;
148
+ }
149
+
150
+ .chevron.expanded {
151
+ transform: rotate(180deg);
152
+ }
153
+
154
+ .sort-panel {
155
+ position: absolute;
156
+ top: calc(100% + 0.5rem);
157
+ left: 0;
158
+ z-index: 20;
159
+ min-width: 400px;
160
+ padding: 1rem;
161
+ background: white;
162
+ border: 1px solid #e5e7eb;
163
+ border-radius: 0.5rem;
164
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
165
+ }
166
+
167
+ .sort-header {
168
+ display: flex;
169
+ justify-content: space-between;
170
+ align-items: center;
171
+ margin-bottom: 0.75rem;
172
+ }
173
+
174
+ .sort-label {
175
+ font-size: 0.875rem;
176
+ font-weight: 600;
177
+ color: #374151;
178
+ }
179
+
180
+ .clear-all-btn {
181
+ font-size: 0.75rem;
182
+ color: #6b7280;
183
+ background: none;
184
+ border: none;
185
+ cursor: pointer;
186
+ padding: 0.25rem 0.5rem;
187
+ border-radius: 0.25rem;
188
+ transition: all 0.2s;
189
+ }
190
+
191
+ .clear-all-btn:hover {
192
+ color: #dc2626;
193
+ background: #fee2e2;
194
+ }
195
+
196
+ .sort-levels {
197
+ display: flex;
198
+ flex-direction: column;
199
+ gap: 0.5rem;
200
+ margin-bottom: 0.75rem;
201
+ }
202
+
203
+ .add-sort-btn {
204
+ display: inline-flex;
205
+ align-items: center;
206
+ gap: 0.5rem;
207
+ padding: 0.5rem 0.75rem;
208
+ font-size: 0.875rem;
209
+ font-weight: 500;
210
+ color: #f59e0b;
211
+ background: white;
212
+ border: 1px dashed #f59e0b;
213
+ border-radius: 0.375rem;
214
+ cursor: pointer;
215
+ transition: all 0.2s;
216
+ }
217
+
218
+ .add-sort-btn:hover:not(:disabled) {
219
+ background: #fef3c7;
220
+ border-style: solid;
221
+ }
222
+
223
+ .add-sort-btn:disabled {
224
+ opacity: 0.5;
225
+ cursor: not-allowed;
226
+ }
227
+
228
+ .icon {
229
+ width: 1rem;
230
+ height: 1rem;
231
+ }
232
+ </style>
@@ -0,0 +1,30 @@
1
+ import { SvelteComponent } from "svelte";
2
+ /**
3
+ * SortBar — Multi-column sort controls.
4
+ *
5
+ * Emits SortConfig[] which maps to ORDER BY in the query builder.
6
+ * Uses ColumnMetadata instead of TanStack ColumnDef.
7
+ */
8
+ import type { SortConfig, ColumnMetadata, ColumnConfig } from '../types.js';
9
+ declare const __propDef: {
10
+ props: {
11
+ columns: ColumnMetadata[];
12
+ columnConfigs?: ColumnConfig[];
13
+ sorting?: SortConfig[];
14
+ onSortingChange: (sorting: SortConfig[]) => void;
15
+ isExpanded?: boolean;
16
+ onExpandedChange?: ((expanded: boolean) => void) | undefined;
17
+ };
18
+ events: {
19
+ [evt: string]: CustomEvent<any>;
20
+ };
21
+ slots: {};
22
+ exports?: {} | undefined;
23
+ bindings?: string | undefined;
24
+ };
25
+ export type SortBarProps = typeof __propDef.props;
26
+ export type SortBarEvents = typeof __propDef.events;
27
+ export type SortBarSlots = typeof __propDef.slots;
28
+ export default class SortBar extends SvelteComponent<SortBarProps, SortBarEvents, SortBarSlots> {
29
+ }
30
+ export {};
@@ -0,0 +1,129 @@
1
+ <script>export let sort;
2
+ export let columns;
3
+ export let columnConfigs = [];
4
+ export let existingSorts;
5
+ export let onUpdate;
6
+ export let onRemove;
7
+ const directionOptions = [
8
+ { value: "asc", label: "A \u2192 Z", icon: "\u2191" },
9
+ { value: "desc", label: "Z \u2192 A", icon: "\u2193" }
10
+ ];
11
+ function getColumnLabel(col) {
12
+ const cfg = columnConfigs.find((c) => c.name === col.name);
13
+ return cfg?.label ?? col.name;
14
+ }
15
+ function handleColumnChange(event) {
16
+ const newColumn = event.target.value;
17
+ onUpdate(newColumn, sort.direction);
18
+ }
19
+ function handleDirectionChange(event) {
20
+ const direction = event.target.value;
21
+ onUpdate(sort.column, direction);
22
+ }
23
+ $: availableColumns = columns.filter((col) => {
24
+ return col.name === sort.column || !existingSorts.some((s) => s.column === col.name);
25
+ });
26
+ $: columnOptions = availableColumns.map((col) => ({
27
+ name: col.name,
28
+ label: getColumnLabel(col)
29
+ }));
30
+ </script>
31
+
32
+ <div class="sort-condition">
33
+ <select class="field-select" value={sort.column} on:change={handleColumnChange}>
34
+ <option value="">Select field...</option>
35
+ {#each columnOptions as option}
36
+ <option value={option.name}>
37
+ {option.label}
38
+ </option>
39
+ {/each}
40
+ </select>
41
+
42
+ <select
43
+ class="direction-select"
44
+ value={sort.direction}
45
+ on:change={handleDirectionChange}
46
+ disabled={!sort.column}
47
+ >
48
+ {#each directionOptions as option}
49
+ <option value={option.value}>
50
+ {option.icon} {option.label}
51
+ </option>
52
+ {/each}
53
+ </select>
54
+
55
+ <button class="remove-btn" on:click={onRemove} title="Remove sort" type="button">
56
+ <svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
57
+ <path
58
+ stroke-linecap="round"
59
+ stroke-linejoin="round"
60
+ stroke-width="2"
61
+ d="M6 18L18 6M6 6l12 12"
62
+ />
63
+ </svg>
64
+ </button>
65
+ </div>
66
+
67
+ <style>
68
+ .sort-condition {
69
+ display: flex;
70
+ align-items: center;
71
+ gap: 0.5rem;
72
+ padding: 0.5rem;
73
+ background: #f9fafb;
74
+ border-radius: 0.375rem;
75
+ }
76
+
77
+ .field-select,
78
+ .direction-select {
79
+ padding: 0.375rem 0.75rem;
80
+ font-size: 0.875rem;
81
+ border: 1px solid #d1d5db;
82
+ border-radius: 0.375rem;
83
+ background: white;
84
+ }
85
+
86
+ .field-select {
87
+ flex: 1;
88
+ min-width: 150px;
89
+ }
90
+
91
+ .direction-select {
92
+ flex: 0.7;
93
+ min-width: 120px;
94
+ }
95
+
96
+ .direction-select:disabled {
97
+ background: #f3f4f6;
98
+ color: #9ca3af;
99
+ cursor: not-allowed;
100
+ }
101
+
102
+ .field-select:focus,
103
+ .direction-select:focus {
104
+ outline: none;
105
+ border-color: #f59e0b;
106
+ box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.1);
107
+ }
108
+
109
+ .remove-btn {
110
+ flex-shrink: 0;
111
+ padding: 0.375rem;
112
+ background: none;
113
+ border: none;
114
+ color: #6b7280;
115
+ cursor: pointer;
116
+ border-radius: 0.25rem;
117
+ transition: all 0.2s;
118
+ }
119
+
120
+ .remove-btn:hover {
121
+ background: #fee2e2;
122
+ color: #dc2626;
123
+ }
124
+
125
+ .icon {
126
+ width: 1rem;
127
+ height: 1rem;
128
+ }
129
+ </style>
@@ -0,0 +1,30 @@
1
+ import { SvelteComponent } from "svelte";
2
+ /**
3
+ * SortCondition — Individual sort row with column picker and direction toggle.
4
+ *
5
+ * PGLite-native: uses ColumnMetadata instead of TanStack ColumnDef.
6
+ * Emits SortConfig (column + direction) instead of TanStack SortingState.
7
+ */
8
+ import type { SortConfig, ColumnMetadata, ColumnConfig } from '../types.js';
9
+ declare const __propDef: {
10
+ props: {
11
+ sort: SortConfig;
12
+ columns: ColumnMetadata[];
13
+ columnConfigs?: ColumnConfig[];
14
+ existingSorts: SortConfig[];
15
+ onUpdate: (column: string, direction: "asc" | "desc") => void;
16
+ onRemove: () => void;
17
+ };
18
+ events: {
19
+ [evt: string]: CustomEvent<any>;
20
+ };
21
+ slots: {};
22
+ exports?: {} | undefined;
23
+ bindings?: string | undefined;
24
+ };
25
+ export type SortConditionProps = typeof __propDef.props;
26
+ export type SortConditionEvents = typeof __propDef.events;
27
+ export type SortConditionSlots = typeof __propDef.slots;
28
+ export default class SortCondition extends SvelteComponent<SortConditionProps, SortConditionEvents, SortConditionSlots> {
29
+ }
30
+ export {};
@@ -0,0 +1,23 @@
1
+ export { default as GridLite } from "./GridLite.svelte";
2
+ export { default as FilterBar } from "./components/FilterBar.svelte";
3
+ export { default as FilterConditionRow } from "./components/FilterCondition.svelte";
4
+ export { default as SortBar } from "./components/SortBar.svelte";
5
+ export { default as SortCondition } from "./components/SortCondition.svelte";
6
+ export { default as GroupBar } from "./components/GroupBar.svelte";
7
+ export { default as CellContextMenu } from "./components/CellContextMenu.svelte";
8
+ export { default as ColumnMenu } from "./components/ColumnMenu.svelte";
9
+ export { default as ColumnPicker } from "./components/ColumnPicker.svelte";
10
+ export { default as RowDetailModal } from "./components/RowDetailModal.svelte";
11
+ export type { GridLiteProps, GridConfig, GridFeatures, GridState, ColumnConfig, ColumnDataType, ColumnMetadata, FilterCondition, FilterOperator, FilterLogic, SortConfig, GroupConfig, AggregationConfig, AggregateFunction, ViewPreset, ClassNameMap, RowHeight, ColumnSpacing, ParameterizedQuery, ToolbarLayout, } from "./types.js";
12
+ export { quoteIdentifier, buildWhereClause, buildOrderByClause, buildGroupByClause, buildPaginationClause, buildGlobalSearchClause, buildGroupSummaryQuery, buildGroupCountQuery, buildGroupDetailQuery, buildQuery, buildCountQuery, } from "./query/builder.js";
13
+ export type { QueryOptions, GroupSummaryOptions, GroupDetailOptions, } from "./query/builder.js";
14
+ export { mapPostgresType, introspectTable, getColumnNames, } from "./query/schema.js";
15
+ export { createLiveQueryStore, createLiveQueryStoreFromQuery, } from "./query/live.js";
16
+ export type { LiveQueryState, LiveQueryStore, PGliteWithLive, } from "./query/live.js";
17
+ export { runMigrations, getLatestVersion, isMigrated, } from "./state/migrations.js";
18
+ export { saveView, loadView, loadViews, loadDefaultView, setDefaultView, deleteView, saveColumnState, loadColumnState, } from "./state/views.js";
19
+ export { getOperatorsForType } from "./utils/filters.js";
20
+ export type { OperatorOption } from "./utils/filters.js";
21
+ export { fuzzyMatch, fuzzySearch, highlightMatches } from "./utils/fuzzy.js";
22
+ export type { FuzzyMatch } from "./utils/fuzzy.js";
23
+ export { formatDate, formatCurrency, formatNumber, formatPercent, } from "./utils/formatters.js";
package/dist/index.js ADDED
@@ -0,0 +1,29 @@
1
+ // Public API for @shotleybuilder/svelte-gridlite-kit
2
+ // Main component
3
+ export { default as GridLite } from "./GridLite.svelte";
4
+ // UI components
5
+ export { default as FilterBar } from "./components/FilterBar.svelte";
6
+ export { default as FilterConditionRow } from "./components/FilterCondition.svelte";
7
+ export { default as SortBar } from "./components/SortBar.svelte";
8
+ export { default as SortCondition } from "./components/SortCondition.svelte";
9
+ export { default as GroupBar } from "./components/GroupBar.svelte";
10
+ export { default as CellContextMenu } from "./components/CellContextMenu.svelte";
11
+ export { default as ColumnMenu } from "./components/ColumnMenu.svelte";
12
+ export { default as ColumnPicker } from "./components/ColumnPicker.svelte";
13
+ export { default as RowDetailModal } from "./components/RowDetailModal.svelte";
14
+ // Query builder
15
+ export { quoteIdentifier, buildWhereClause, buildOrderByClause, buildGroupByClause, buildPaginationClause, buildGlobalSearchClause, buildGroupSummaryQuery, buildGroupCountQuery, buildGroupDetailQuery, buildQuery, buildCountQuery, } from "./query/builder.js";
16
+ // Schema introspection
17
+ export { mapPostgresType, introspectTable, getColumnNames, } from "./query/schema.js";
18
+ // Live query store
19
+ export { createLiveQueryStore, createLiveQueryStoreFromQuery, } from "./query/live.js";
20
+ // State persistence — migrations
21
+ export { runMigrations, getLatestVersion, isMigrated, } from "./state/migrations.js";
22
+ // State persistence — views
23
+ export { saveView, loadView, loadViews, loadDefaultView, setDefaultView, deleteView, saveColumnState, loadColumnState, } from "./state/views.js";
24
+ // Filter utilities
25
+ export { getOperatorsForType } from "./utils/filters.js";
26
+ // Fuzzy search utilities
27
+ export { fuzzyMatch, fuzzySearch, highlightMatches } from "./utils/fuzzy.js";
28
+ // Formatters
29
+ export { formatDate, formatCurrency, formatNumber, formatPercent, } from "./utils/formatters.js";
@@ -0,0 +1,160 @@
1
+ /**
2
+ * SQL Query Builder for GridLite
3
+ *
4
+ * Translates FilterCondition[], SortConfig[], GroupConfig[], and pagination
5
+ * into parameterized SQL queries. All user input is parameterized — never
6
+ * interpolated into SQL strings.
7
+ *
8
+ * Column names are validated against an allowlist (from schema introspection)
9
+ * to prevent SQL injection via identifier manipulation.
10
+ */
11
+ import type { FilterCondition, FilterLogic, SortConfig, GroupConfig, ParameterizedQuery } from "../types.js";
12
+ /**
13
+ * Validate and quote a column name as a SQL identifier.
14
+ * Throws if the name is not a valid identifier.
15
+ *
16
+ * When `allowedColumns` is provided, also checks the name is in the allowlist.
17
+ */
18
+ export declare function quoteIdentifier(name: string, allowedColumns?: string[]): string;
19
+ /**
20
+ * Build a WHERE clause from filter conditions.
21
+ *
22
+ * Returns `{ sql: string, params: unknown[] }` where sql is empty string
23
+ * if there are no valid conditions.
24
+ *
25
+ * @param conditions - Filter conditions from the UI
26
+ * @param logic - 'and' or 'or' between conditions
27
+ * @param paramOffset - Starting parameter index (for $1, $2, ...) when composing with other clauses
28
+ * @param allowedColumns - Optional allowlist of column names from schema introspection
29
+ */
30
+ export declare function buildWhereClause(conditions: FilterCondition[], logic?: FilterLogic, paramOffset?: number, allowedColumns?: string[]): ParameterizedQuery;
31
+ /**
32
+ * Build an ORDER BY clause from sort configurations.
33
+ */
34
+ export declare function buildOrderByClause(sorting: SortConfig[], allowedColumns?: string[]): string;
35
+ /**
36
+ * Build GROUP BY clause and adjust SELECT for grouped queries.
37
+ *
38
+ * Returns the GROUP BY clause and the SELECT column list.
39
+ */
40
+ export declare function buildGroupByClause(grouping: GroupConfig[], allowedColumns?: string[]): {
41
+ selectColumns: string;
42
+ groupBy: string;
43
+ };
44
+ /**
45
+ * Build LIMIT/OFFSET clause for pagination.
46
+ */
47
+ export declare function buildPaginationClause(page: number, pageSize: number): string;
48
+ /**
49
+ * Build a WHERE-compatible clause for global search across text columns.
50
+ *
51
+ * Generates `(col1::text ILIKE '%' || $N || '%' OR col2::text ILIKE ...)`
52
+ * using a single parameter for the search term. The `::text` cast allows
53
+ * searching non-text columns (numbers, dates, booleans) as strings.
54
+ *
55
+ * @param searchTerm - The search string
56
+ * @param textColumns - Column names to search across
57
+ * @param paramOffset - Starting parameter index
58
+ * @param allowedColumns - Optional allowlist
59
+ */
60
+ export declare function buildGlobalSearchClause(searchTerm: string, textColumns: string[], paramOffset?: number, allowedColumns?: string[]): ParameterizedQuery;
61
+ export interface GroupSummaryOptions {
62
+ /** Table name */
63
+ table: string;
64
+ /** Group columns (max 3) */
65
+ grouping: GroupConfig[];
66
+ /** Filter conditions to apply before grouping */
67
+ filters?: FilterCondition[];
68
+ /** Filter logic */
69
+ filterLogic?: FilterLogic;
70
+ /** Allowed column names */
71
+ allowedColumns?: string[];
72
+ /** Global search term */
73
+ globalSearch?: string;
74
+ /** Columns to search for global search */
75
+ searchColumns?: string[];
76
+ /** Sorting for the group results */
77
+ sorting?: SortConfig[];
78
+ /** Pagination for groups */
79
+ page?: number;
80
+ /** Page size for groups */
81
+ pageSize?: number;
82
+ }
83
+ /**
84
+ * Build a query that returns distinct group values with COUNT(*) and
85
+ * optional aggregations. Used for the group header rows.
86
+ *
87
+ * Example output for grouping by department with avg(salary):
88
+ * SELECT "department", COUNT(*) AS "_count", AVG("salary") AS "avg_salary"
89
+ * FROM "employees" WHERE ... GROUP BY "department" ORDER BY "department"
90
+ */
91
+ export declare function buildGroupSummaryQuery(options: GroupSummaryOptions): ParameterizedQuery;
92
+ /**
93
+ * Build a count query for group summaries (how many groups exist).
94
+ */
95
+ export declare function buildGroupCountQuery(options: GroupSummaryOptions): ParameterizedQuery;
96
+ export interface GroupDetailOptions {
97
+ /** Table name */
98
+ table: string;
99
+ /** Values to match for parent groups: [{ column: "department", value: "Engineering" }] */
100
+ groupValues: {
101
+ column: string;
102
+ value: unknown;
103
+ }[];
104
+ /** Filter conditions */
105
+ filters?: FilterCondition[];
106
+ /** Filter logic */
107
+ filterLogic?: FilterLogic;
108
+ /** Sorting for child rows */
109
+ sorting?: SortConfig[];
110
+ /** Allowed column names */
111
+ allowedColumns?: string[];
112
+ /** Global search term */
113
+ globalSearch?: string;
114
+ /** Columns to search */
115
+ searchColumns?: string[];
116
+ }
117
+ /**
118
+ * Build a query that returns the detail rows for an expanded group.
119
+ *
120
+ * Adds WHERE constraints for each group column value on top of any
121
+ * existing filters. Used when a user expands a group header.
122
+ *
123
+ * Example: grouping by department, expanded "Engineering":
124
+ * SELECT * FROM "employees" WHERE ... AND "department" = $N ORDER BY ...
125
+ */
126
+ export declare function buildGroupDetailQuery(options: GroupDetailOptions): ParameterizedQuery;
127
+ export interface QueryOptions {
128
+ /** Table name to query */
129
+ table: string;
130
+ /** Filter conditions */
131
+ filters?: FilterCondition[];
132
+ /** Filter logic (and/or) */
133
+ filterLogic?: FilterLogic;
134
+ /** Sort configuration */
135
+ sorting?: SortConfig[];
136
+ /** Group configuration */
137
+ grouping?: GroupConfig[];
138
+ /** Current page (0-indexed) */
139
+ page?: number;
140
+ /** Page size */
141
+ pageSize?: number;
142
+ /** Allowed column names (from schema introspection) */
143
+ allowedColumns?: string[];
144
+ /** Global search term (ILIKE across searchColumns) */
145
+ globalSearch?: string;
146
+ /** Columns to search for global search (defaults to all allowedColumns) */
147
+ searchColumns?: string[];
148
+ }
149
+ /**
150
+ * Build a complete parameterized SELECT query from grid state.
151
+ *
152
+ * This is the main entry point for the query builder. It composes
153
+ * WHERE, ORDER BY, GROUP BY, and LIMIT/OFFSET clauses into a single query.
154
+ */
155
+ export declare function buildQuery(options: QueryOptions): ParameterizedQuery;
156
+ /**
157
+ * Build a COUNT query for pagination total.
158
+ * Uses the same filters but no sorting/grouping/pagination.
159
+ */
160
+ export declare function buildCountQuery(options: QueryOptions): ParameterizedQuery;