@navette-js/tables 0.0.0-stage → 1.0.0-beta.2

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.
@@ -0,0 +1,302 @@
1
+ import type { ListQuery } from '@navette-js/database';
2
+ import { Block, Builder } from '@navette-js/forms/builder';
3
+ import { Component } from '@navette-js/schemas';
4
+ import type { FilterIndicator, FilterState, TableFilter, TableHost } from './index.ts';
5
+ /** Limits applied to the rule tree received from the browser. */
6
+ export declare const MAX_RULES = 100;
7
+ export declare const MAX_NESTING_DEPTH = 3;
8
+ /** Maximum number of values in a multiple select. */
9
+ export declare const MAX_LIST_VALUES = 100;
10
+ type Comparison = '=' | '!=' | '<' | '<=' | '>' | '>=';
11
+ type Key = string | number;
12
+ export type Condition = {
13
+ kind: 'like';
14
+ column: string;
15
+ pattern: string;
16
+ negate: boolean;
17
+ } | {
18
+ kind: 'compare';
19
+ column: string;
20
+ operator: Comparison;
21
+ value: string | number;
22
+ } | {
23
+ kind: 'boolean';
24
+ column: string;
25
+ value: boolean;
26
+ } | {
27
+ kind: 'date' | 'month' | 'year';
28
+ column: string;
29
+ operator: Comparison;
30
+ value: string | number;
31
+ }
32
+ /** Value is filled (neither null nor an empty string), or the opposite. */
33
+ | {
34
+ kind: 'filled';
35
+ column: string;
36
+ negate: boolean;
37
+ } | {
38
+ kind: 'in';
39
+ column: string;
40
+ values: Key[];
41
+ negate: boolean;
42
+ }
43
+ /** Number of related records compared to a value. */
44
+ | {
45
+ kind: 'relationCount';
46
+ relationship: string;
47
+ operator: Comparison;
48
+ value: number;
49
+ modify?: RelationshipQueryModifier;
50
+ }
51
+ /** At least one related record (among `keys` when given), or none (`negate`). */
52
+ | {
53
+ kind: 'related';
54
+ relationship: string;
55
+ keys: Key[] | null;
56
+ negate: boolean;
57
+ modify?: RelationshipQueryModifier;
58
+ };
59
+ /** `{ relationship, nodes }`: the nodes apply to the related records (`whereExists` subquery). */
60
+ export type ConditionNode = Condition | {
61
+ or: ConditionNode[][];
62
+ } | {
63
+ relationship: string;
64
+ nodes: ConditionNode[];
65
+ modify?: RelationshipQueryModifier;
66
+ };
67
+ type Settings = Record<string, unknown>;
68
+ type Resolved = {
69
+ condition: Condition;
70
+ summary: string;
71
+ };
72
+ /** Constraint operator: its settings form and how it turns into a condition. */
73
+ type Operator = {
74
+ name: string;
75
+ label: (inverse: boolean) => string;
76
+ /** Default settings (null: the operator has no form). */
77
+ defaults: () => Settings | null;
78
+ fields: () => Component[];
79
+ /** Null when the settings fail the form's validation: the rule then has no effect. */
80
+ resolve: (settings: Settings, inverse: boolean, constraint: Constraint) => Resolved | null;
81
+ /** Condition applied to the base query even when the constraint targets a relationship (RelationshipConstraint operators). */
82
+ onBaseQuery?: boolean;
83
+ };
84
+ /** Date (or date-time, with `hasTime`) targeted by an "after / before" operator's settings. */
85
+ export declare function resolveRuleDate(settings: Settings, now?: Date, hasTime?: boolean): string | null;
86
+ declare const OPERATORS: Record<'text' | 'number' | 'boolean' | 'date', Operator[]>;
87
+ type ConstraintType = keyof typeof OPERATORS | 'select' | 'relationship';
88
+ /** Application-defined modifier of the related records query (knex / objection). */
89
+ export type RelationshipQueryModifier = (query: any) => void;
90
+ /**
91
+ * Attribute the query builder can filter on. The queried column and relationship are the ones declared here, never browser values:
92
+ * the browser only picks a constraint (by name) and one of its operators.
93
+ */
94
+ export declare class Constraint {
95
+ readonly name: string;
96
+ readonly type: ConstraintType;
97
+ private _label;
98
+ private _attributeLabel;
99
+ private _icon;
100
+ private _column;
101
+ protected _relationship: string | null;
102
+ protected _modifyRelationshipQuery: RelationshipQueryModifier | null;
103
+ protected _nullable: boolean;
104
+ /** Objection model of the table's records, used to list related records. */
105
+ protected _model: any;
106
+ constructor(name: string, type: ConstraintType, icon: string);
107
+ label(label: string): this;
108
+ /** Attribute name used in rule summaries (defaults to the label). */
109
+ attributeLabel(label: string | null): this;
110
+ icon(name: string): this;
111
+ /** Queried column (or query alias) when it differs from the name. */
112
+ attribute(column: string): this;
113
+ /**
114
+ * Filters on an attribute of related records: `relationship('author', 'name')` filters on the author's name.
115
+ * The relationship must be declared by the Objection model (`relationMappings`).
116
+ */
117
+ relationship(name: string, titleAttribute: string, modifyQueryUsing?: RelationshipQueryModifier | null): this;
118
+ modifyRelationshipQueryUsing(callback: RelationshipQueryModifier | null): this;
119
+ /** Adds the "Is filled" / "Is blank" operator. */
120
+ nullable(condition?: boolean): this;
121
+ isNullable(): boolean;
122
+ model(model: any): this;
123
+ getModel(): any;
124
+ getLabel(): string;
125
+ getAttributeLabel(): string;
126
+ getIcon(): string;
127
+ getColumn(): string;
128
+ /** Relationship whose records hold the attribute, or `null` for a column of the table. */
129
+ getRelationshipName(): string | null;
130
+ getModifyRelationshipQueryUsing(): RelationshipQueryModifier | null;
131
+ getOperators(): Operator[];
132
+ /** `contains` or `contains.inverse` → operator and direction; null if the constraint does not declare it. */
133
+ parseOperator(value: unknown): {
134
+ operator: Operator;
135
+ inverse: boolean;
136
+ } | null;
137
+ getOperatorOptions(): [string, string][];
138
+ /** Setting names known to the constraint across all operators (switching operator keeps the previous settings). */
139
+ getSettingNames(): string[];
140
+ /** Settings whose value is a list (multiple select). */
141
+ getListSettingNames(): string[];
142
+ getDefaultData(): Record<string, unknown>;
143
+ }
144
+ export declare class TextConstraint extends Constraint {
145
+ static make(name: string): TextConstraint;
146
+ }
147
+ export declare class NumberConstraint extends Constraint {
148
+ static make(name: string): NumberConstraint;
149
+ }
150
+ export declare class BooleanConstraint extends Constraint {
151
+ static make(name: string): BooleanConstraint;
152
+ }
153
+ /** Date constraint; with `time()`, date and time (date-time picker, minute and hour presets and units). */
154
+ export declare class DateConstraint extends Constraint {
155
+ private _time;
156
+ static make(name: string): DateConstraint;
157
+ time(condition?: boolean): this;
158
+ hasTime(): boolean;
159
+ getOperators(): Operator[];
160
+ }
161
+ /** Value picked from a fixed list; with `multiple()`, any of the picked values. */
162
+ export declare class SelectConstraint extends Constraint {
163
+ private _options;
164
+ private _multiple;
165
+ private _searchable;
166
+ static make(name: string): SelectConstraint;
167
+ /** Options as `[value, label]` pairs or `{ value: label }`; only these values can filter. */
168
+ options(options: [string | number, string][] | Record<string, string>): this;
169
+ getOptions(): [string, string][];
170
+ multiple(condition?: boolean): this;
171
+ isMultiple(): boolean;
172
+ searchable(condition?: boolean): this;
173
+ getOperators(): Operator[];
174
+ }
175
+ /**
176
+ * "Is" / "Contains" operator of a RelationshipConstraint: related records are picked by key from a list loaded from the related model;
177
+ * `titleAttribute` is the displayed column.
178
+ */
179
+ export declare class IsRelatedToOperator {
180
+ private _titleAttribute;
181
+ private _multiple;
182
+ private _searchable;
183
+ private _preload;
184
+ private _optionsLimit;
185
+ private _modifyQuery;
186
+ static make(): IsRelatedToOperator;
187
+ titleAttribute(attribute: string | null): this;
188
+ getTitleAttribute(): string | null;
189
+ multiple(condition?: boolean): this;
190
+ isMultiple(): boolean;
191
+ searchable(condition?: boolean): this;
192
+ preload(condition?: boolean): this;
193
+ optionsLimit(limit: number): this;
194
+ modifyRelationshipQueryUsing(callback: RelationshipQueryModifier | null): this;
195
+ getModifyRelationshipQueryUsing(): RelationshipQueryModifier | null;
196
+ /** Builds the query builder operator for the given constraint. */
197
+ toOperator(constraint: RelationshipConstraint): Operator;
198
+ getOptionsLimit(): number;
199
+ }
200
+ /**
201
+ * Related records: minimum, maximum or exact count (`multiple()`), empty relationship (`emptyable()`, on by default with `multiple()`),
202
+ * or related to picked records (`selectable(IsRelatedToOperator.make()…)`).
203
+ */
204
+ export declare class RelationshipConstraint extends Constraint {
205
+ private _multiple;
206
+ private _emptyable;
207
+ private _selectable;
208
+ /** Labels of the related records cited by rules, loaded before indicators are computed. */
209
+ private _relatedLabels;
210
+ static make(name: string): RelationshipConstraint;
211
+ multiple(condition?: boolean): this;
212
+ isMultiple(): boolean;
213
+ emptyable(condition?: boolean | null): this;
214
+ canBeEmpty(): boolean;
215
+ selectable(operator: IsRelatedToOperator): this;
216
+ getSelectableOperator(): IsRelatedToOperator | null;
217
+ /** Queried relationship: from `relationship()`, else `attribute()`, else the constraint name. */
218
+ getRelationshipName(): string;
219
+ getOperators(): Operator[];
220
+ getRelatedLabel(key: Key): string | null;
221
+ /** Related model, found through the table model's relationship (declared, or the host's Objection adapter model). */
222
+ private relatedModel;
223
+ private relatedQuery;
224
+ /** Select options: records of the related model, sorted by title. */
225
+ loadRelatedOptions(operator: IsRelatedToOperator, host: unknown): Promise<[string, string][]>;
226
+ /** Loads labels of the keys cited by rules (for indicators such as "Author is Ada or Linus"). */
227
+ loadRelatedLabels(keys: Key[], host: unknown): Promise<void>;
228
+ }
229
+ type Rule = {
230
+ type: string;
231
+ data: Record<string, any>;
232
+ };
233
+ type Rules = Record<string, Rule>;
234
+ /**
235
+ * Reduces the browser's rule tree to what the filter declares: known block types, the constraint's operators, scalar settings with
236
+ * known names, bounded depth and rule count. An unknown operator is reset to null (the rule has no effect).
237
+ */
238
+ export declare function sanitizeRules(constraints: Constraint[], raw: unknown, depth?: number, budget?: {
239
+ rules: number;
240
+ }): Rules;
241
+ /** Sanitized rules → conditions and indicator labels (one entry per effective top-level rule). */
242
+ export declare function compileRules(constraints: Constraint[], rules: Rules, iteration?: number): {
243
+ nodes: ConditionNode[];
244
+ summaries: Record<string, string>;
245
+ };
246
+ /** SQL dialect of the query's knex connection. */
247
+ export type SqlDialect = 'sqlite' | 'mysql' | 'pg' | 'mssql';
248
+ /** Dialect read from the knex client (`query.client.config.client`, or the Objection model's connection); SQLite by default. */
249
+ export declare function sqlDialectOf(query: any): SqlDialect;
250
+ /**
251
+ * Applies conditions to a knex / objection query. Columns are bound as identifiers and values as parameters; comparison operators
252
+ * come from a closed list. Date and search SQL follows the connection's dialect.
253
+ */
254
+ export declare function applyConditions(query: any, nodes: ConditionNode[], dialect?: SqlDialect): void;
255
+ /** Rule builder: a Builder whose blocks are the filter's constraints, plus the "OR" block. */
256
+ export declare class RuleBuilder extends Builder {
257
+ readonly filter: QueryBuilder;
258
+ readonly depth: number;
259
+ constructor(name: string, filter: QueryBuilder, depth?: number);
260
+ /** The "OR" block is neither offered nor accepted beyond the maximum depth; nothing is offered once the rule limit is reached. */
261
+ getBlockPickerBlocks(): Block[];
262
+ getBlock(name: unknown): Block | undefined;
263
+ protected canAdd(): boolean;
264
+ /** Counts rules across the whole tree ("OR" blocks excluded), read from the host's deferred filter state. */
265
+ isAtRuleLimit(): boolean;
266
+ /** Component action without modal (also used by "OR" groups). */
267
+ makeAction(name: string, handler: (args: Record<string, unknown>) => void): any;
268
+ }
269
+ /** Query builder table filter. */
270
+ export declare class QueryBuilder implements TableFilter {
271
+ readonly name = "queryBuilder";
272
+ private _constraints;
273
+ private _pickerColumns;
274
+ private _model;
275
+ static make(): QueryBuilder;
276
+ constraints(constraints: Constraint[]): this;
277
+ /** Objection model of the table, passed to relationship constraints (related record lists). */
278
+ model(model: any): this;
279
+ /** Before indicators: loads labels of the related records cited by rules (`RelationshipConstraint.selectable`). */
280
+ loadIndicatorData(state: FilterState, host: TableHost): Promise<void>;
281
+ constraintPickerColumns(columns?: number | null): this;
282
+ getConstraints(): Constraint[];
283
+ getConstraintPickerColumns(): number | null;
284
+ getLabel(): string;
285
+ getDefaultState(): FilterState;
286
+ getFormDefinition(): {
287
+ placeholder: string;
288
+ options: [string, string][];
289
+ };
290
+ /** The filter spans the full width of the filters form. */
291
+ getColumnSpan(): Record<string, number | string>;
292
+ getFormComponents(): Component[];
293
+ sanitizeState(state: unknown): FilterState;
294
+ countRules(state: unknown): number;
295
+ private compile;
296
+ isActive(state: FilterState): boolean;
297
+ getIndicators(state: FilterState): FilterIndicator[];
298
+ /** Removing an indicator removes the matching top-level rule. */
299
+ removeIndicator(state: FilterState, field: string): FilterState;
300
+ apply(query: Omit<ListQuery, 'page' | 'perPage'>, state: FilterState): void;
301
+ }
302
+ export {};