@rastr/filter-builder 0.1.0-beta.2 → 0.1.0-beta.3
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.
- package/dist/index.cjs +122 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +120 -3
- package/dist/index.js.map +1 -1
- package/dist/styles.css +19 -0
- package/package.json +8 -3
package/dist/index.cjs
CHANGED
|
@@ -173,7 +173,7 @@ function updateNode(draft, path, patch, columns = []) {
|
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
// src/builder.ts
|
|
176
|
-
var
|
|
176
|
+
var import_core3 = require("@rastr/core");
|
|
177
177
|
var import_controls = require("@rastr/controls");
|
|
178
178
|
|
|
179
179
|
// src/dom.ts
|
|
@@ -191,6 +191,7 @@ function h(tag, props = {}, ...children) {
|
|
|
191
191
|
}
|
|
192
192
|
|
|
193
193
|
// src/i18n.ts
|
|
194
|
+
var import_core2 = require("@rastr/core");
|
|
194
195
|
var cs = {
|
|
195
196
|
label: "Filtr",
|
|
196
197
|
and: "A",
|
|
@@ -269,10 +270,126 @@ var en = {
|
|
|
269
270
|
isNotNull: "Is not blank"
|
|
270
271
|
}
|
|
271
272
|
};
|
|
272
|
-
var
|
|
273
|
+
var de = {
|
|
274
|
+
label: "Filter",
|
|
275
|
+
and: "Und",
|
|
276
|
+
or: "Oder",
|
|
277
|
+
nand: "Nicht und",
|
|
278
|
+
nor: "Nicht oder",
|
|
279
|
+
negate: "Negieren",
|
|
280
|
+
addCondition: "Bedingung hinzuf\xFCgen",
|
|
281
|
+
addGroup: "Gruppe hinzuf\xFCgen",
|
|
282
|
+
remove: "Entfernen",
|
|
283
|
+
field: "Feld",
|
|
284
|
+
operator: "Operator",
|
|
285
|
+
value: "Wert",
|
|
286
|
+
from: "Von",
|
|
287
|
+
to: "Bis",
|
|
288
|
+
choose: "(ausw\xE4hlen)",
|
|
289
|
+
yes: "Ja",
|
|
290
|
+
no: "Nein",
|
|
291
|
+
blanks: "(Leer)",
|
|
292
|
+
search: "Suchen\u2026",
|
|
293
|
+
empty: "Keine Bedingungen",
|
|
294
|
+
ops: {
|
|
295
|
+
eq: "Gleich",
|
|
296
|
+
neq: "Ungleich",
|
|
297
|
+
lt: "Kleiner als",
|
|
298
|
+
lte: "Kleiner oder gleich",
|
|
299
|
+
gt: "Gr\xF6\xDFer als",
|
|
300
|
+
gte: "Gr\xF6\xDFer oder gleich",
|
|
301
|
+
between: "Zwischen",
|
|
302
|
+
in: "Ist einer von",
|
|
303
|
+
notIn: "Ist keiner von",
|
|
304
|
+
contains: "Enth\xE4lt",
|
|
305
|
+
notContains: "Enth\xE4lt nicht",
|
|
306
|
+
startsWith: "Beginnt mit",
|
|
307
|
+
endsWith: "Endet mit",
|
|
308
|
+
isNull: "Ist leer",
|
|
309
|
+
isNotNull: "Ist nicht leer"
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
var pl = {
|
|
313
|
+
label: "Filtr",
|
|
314
|
+
and: "I",
|
|
315
|
+
or: "Lub",
|
|
316
|
+
nand: "Nie i",
|
|
317
|
+
nor: "Nie lub",
|
|
318
|
+
negate: "Zaneguj",
|
|
319
|
+
addCondition: "Dodaj warunek",
|
|
320
|
+
addGroup: "Dodaj grup\u0119",
|
|
321
|
+
remove: "Usu\u0144",
|
|
322
|
+
field: "Pole",
|
|
323
|
+
operator: "Operator",
|
|
324
|
+
value: "Warto\u015B\u0107",
|
|
325
|
+
from: "Od",
|
|
326
|
+
to: "Do",
|
|
327
|
+
choose: "(wybierz)",
|
|
328
|
+
yes: "Tak",
|
|
329
|
+
no: "Nie",
|
|
330
|
+
blanks: "(Puste)",
|
|
331
|
+
search: "Szukaj\u2026",
|
|
332
|
+
empty: "Brak warunk\xF3w",
|
|
333
|
+
ops: {
|
|
334
|
+
eq: "R\xF3wna si\u0119",
|
|
335
|
+
neq: "Nie r\xF3wna si\u0119",
|
|
336
|
+
lt: "Mniejsze ni\u017C",
|
|
337
|
+
lte: "Mniejsze lub r\xF3wne",
|
|
338
|
+
gt: "Wi\u0119ksze ni\u017C",
|
|
339
|
+
gte: "Wi\u0119ksze lub r\xF3wne",
|
|
340
|
+
between: "Pomi\u0119dzy",
|
|
341
|
+
in: "Jest jednym z",
|
|
342
|
+
notIn: "Nie jest \u017Cadnym z",
|
|
343
|
+
contains: "Zawiera",
|
|
344
|
+
notContains: "Nie zawiera",
|
|
345
|
+
startsWith: "Zaczyna si\u0119 od",
|
|
346
|
+
endsWith: "Ko\u0144czy si\u0119 na",
|
|
347
|
+
isNull: "Jest puste",
|
|
348
|
+
isNotNull: "Nie jest puste"
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
var sk = {
|
|
352
|
+
label: "Filter",
|
|
353
|
+
and: "A",
|
|
354
|
+
or: "Alebo",
|
|
355
|
+
nand: "Nie a",
|
|
356
|
+
nor: "Nie alebo",
|
|
357
|
+
negate: "Negova\u0165",
|
|
358
|
+
addCondition: "Prida\u0165 podmienku",
|
|
359
|
+
addGroup: "Prida\u0165 skupinu",
|
|
360
|
+
remove: "Odstr\xE1ni\u0165",
|
|
361
|
+
field: "Pole",
|
|
362
|
+
operator: "Oper\xE1tor",
|
|
363
|
+
value: "Hodnota",
|
|
364
|
+
from: "Od",
|
|
365
|
+
to: "Do",
|
|
366
|
+
choose: "(vyberte)",
|
|
367
|
+
yes: "\xC1no",
|
|
368
|
+
no: "Nie",
|
|
369
|
+
blanks: "(Pr\xE1zdne)",
|
|
370
|
+
search: "H\u013Eada\u0165\u2026",
|
|
371
|
+
empty: "\u017Diadne podmienky",
|
|
372
|
+
ops: {
|
|
373
|
+
eq: "Rovn\xE1 sa",
|
|
374
|
+
neq: "Nerovn\xE1 sa",
|
|
375
|
+
lt: "Men\u0161ie ako",
|
|
376
|
+
lte: "Men\u0161ie alebo rovn\xE9",
|
|
377
|
+
gt: "V\xE4\u010D\u0161ie ako",
|
|
378
|
+
gte: "V\xE4\u010D\u0161ie alebo rovn\xE9",
|
|
379
|
+
between: "Medzi",
|
|
380
|
+
in: "Je jedna z",
|
|
381
|
+
notIn: "Nie je \u017Eiadna z",
|
|
382
|
+
contains: "Obsahuje",
|
|
383
|
+
notContains: "Neobsahuje",
|
|
384
|
+
startsWith: "Za\u010D\xEDna na",
|
|
385
|
+
endsWith: "Kon\u010D\xED na",
|
|
386
|
+
isNull: "Je pr\xE1zdne",
|
|
387
|
+
isNotNull: "Nie je pr\xE1zdne"
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
var FILTER_BUILDER_MESSAGES = { cs, en, de, pl, sk };
|
|
273
391
|
function resolveFilterBuilderMessages(locale, overrides) {
|
|
274
|
-
|
|
275
|
-
return { ...language === "en" ? en : cs, ...overrides };
|
|
392
|
+
return { ...FILTER_BUILDER_MESSAGES[(0, import_core2.messageLanguage)(locale)], ...overrides };
|
|
276
393
|
}
|
|
277
394
|
|
|
278
395
|
// src/builder.ts
|
|
@@ -325,7 +442,7 @@ function createFilterBuilder(container, options) {
|
|
|
325
442
|
let pendingFocus = null;
|
|
326
443
|
function canonicalize(value) {
|
|
327
444
|
try {
|
|
328
|
-
return (0,
|
|
445
|
+
return (0, import_core3.canonicalizeFilter)(value, columns);
|
|
329
446
|
} catch {
|
|
330
447
|
return value;
|
|
331
448
|
}
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/tree.ts","../src/builder.ts","../src/dom.ts","../src/i18n.ts"],"sourcesContent":["export { addCondition, addGroup, fromDraft, MAX_DEPTH, operatorsFor, removeNode, toDraft, updateNode } from './tree';\nexport type { DraftCondition, DraftGroup, DraftNode, FilterPath, GroupPatch } from './tree';\nexport { createFilterBuilder, TEXT_DEBOUNCE_MS } from './builder';\nexport type { FilterBuilder, FilterBuilderOptions } from './builder';\nexport { FILTER_BUILDER_MESSAGES, resolveFilterBuilderMessages } from './i18n';\nexport type { FilterBuilderMessages } from './i18n';\n","import { and, canonicalizeFilter, cond, isCondition, isGroup, isNot, normalizeFilter, not, or } from '@rastr/core';\nimport type { ColumnDef, ConditionNode, ConditionOp, DataType, FilterNode } from '@rastr/core';\n\n/** Indexes from the root group to a node. */\nexport type FilterPath = readonly number[];\n\nexport interface DraftCondition {\n readonly field: string | null;\n readonly op: ConditionOp | null;\n readonly value?: unknown;\n}\n\nexport interface DraftGroup {\n readonly kind: 'group';\n readonly op: 'and' | 'or';\n readonly negated: boolean;\n readonly items: readonly DraftNode[];\n}\n\nexport type DraftNode = DraftGroup | ({ readonly kind: 'condition' } & DraftCondition);\n\nexport type GroupPatch = { readonly op?: 'and' | 'or'; readonly negated?: boolean };\n\n/** Groups can be nested up to this many levels including the root. */\nexport const MAX_DEPTH: number = 4;\n\nconst TEXT_OPS: readonly ConditionOp[] = ['contains', 'notContains', 'startsWith', 'endsWith', 'eq', 'neq'];\nconst RANGE_OPS: readonly ConditionOp[] = ['eq', 'neq', 'lt', 'lte', 'gt', 'gte', 'between'];\nconst LIST_OPS: readonly ConditionOp[] = ['in', 'notIn'];\nconst NULL_OPS: readonly ConditionOp[] = ['isNull', 'isNotNull'];\n\n/** Operators offered for a column: the filter row's list plus in/notIn and null tests. */\nexport function operatorsFor(column: ColumnDef<unknown>): readonly ConditionOp[] {\n if (column.lookup) return ['eq', 'neq', ...LIST_OPS, ...NULL_OPS];\n const dataType: DataType = column.dataType ?? 'string';\n if (dataType === 'boolean') return ['eq', ...NULL_OPS];\n if (dataType === 'number' || dataType === 'date' || dataType === 'datetime') return [...RANGE_OPS, ...LIST_OPS, ...NULL_OPS];\n return [...TEXT_OPS, ...LIST_OPS, ...NULL_OPS];\n}\n\n/** How many values an operator takes; a changed arity clears the value. */\nexport function arityOf(op: ConditionOp | null): 'none' | 'single' | 'pair' | 'list' {\n if (op === null || op === 'isNull' || op === 'isNotNull') return 'none';\n if (op === 'between') return 'pair';\n if (op === 'in' || op === 'notIn') return 'list';\n return 'single';\n}\n\nconst emptyGroup = (op: 'and' | 'or' = 'and', negated: boolean = false, items: readonly DraftNode[] = []): DraftGroup => ({\n kind: 'group',\n op,\n negated,\n items,\n});\n\nconst draftCondition = (node: ConditionNode): DraftNode =>\n node.value === undefined ? { kind: 'condition', field: node.field, op: node.op } : { kind: 'condition', field: node.field, op: node.op, value: node.value };\n\n/** or(in, isNull) on one field, as written by the header filter with \"(Blanks)\" selected. */\nfunction foldBlanks(items: readonly FilterNode[]): ConditionNode | null {\n if (items.length !== 2 || !items.every(isCondition)) return null;\n const [first, second] = items as readonly ConditionNode[];\n const list: ConditionNode | undefined = [first, second].find(c => c?.op === 'in');\n const blanks: ConditionNode | undefined = [first, second].find(c => c?.op === 'isNull');\n if (!list || !blanks || list.field !== blanks.field || !Array.isArray(list.value)) return null;\n return cond(list.field, 'in', [...(list.value as unknown[]), null]);\n}\n\nfunction toDraftNode(node: FilterNode): DraftNode {\n if (isNot(node)) {\n const inner: DraftNode = toDraftNode(node.item);\n return inner.kind === 'group' ? { ...inner, negated: !inner.negated } : emptyGroup('and', true, [inner]);\n }\n if (isGroup(node)) {\n const folded: ConditionNode | null = node.op === 'or' ? foldBlanks(node.items) : null;\n if (folded) return draftCondition(folded);\n return emptyGroup(node.op, false, node.items.map(toDraftNode));\n }\n return draftCondition(node);\n}\n\n/** Editable tree for a filter; the root is always a group. */\nexport function toDraft(filter: FilterNode | null): DraftNode {\n const normalized: FilterNode | null = normalizeFilter(filter);\n if (normalized === null) return emptyGroup();\n const draft: DraftNode = toDraftNode(normalized);\n return draft.kind === 'group' ? draft : emptyGroup('and', false, [draft]);\n}\n\nconst isBlankValue = (value: unknown): boolean => value === undefined || value === null || value === '';\n\nfunction conditionFrom(node: DraftCondition): FilterNode | null {\n if (node.field === null || node.op === null) return null;\n const { field, op, value } = node;\n switch (arityOf(op)) {\n case 'none':\n return cond(field, op);\n case 'single':\n return isBlankValue(value) ? null : cond(field, op, value);\n case 'pair': {\n if (!Array.isArray(value) || value.length !== 2 || value.some(isBlankValue)) return null;\n return cond(field, op, value);\n }\n case 'list': {\n if (!Array.isArray(value)) return null;\n const values: unknown[] = value.filter(v => v !== null && v !== undefined);\n const blanks: boolean = values.length !== value.length;\n const list: FilterNode | null = values.length > 0 ? cond(field, op, values) : null;\n if (!blanks) return list;\n // A blanks entry is the null test the header filter writes next to the list.\n return op === 'in' ? or(list, cond(field, 'isNull')) : and(list, cond(field, 'isNotNull'));\n }\n }\n}\n\nfunction fromDraftNode(node: DraftNode): FilterNode | null {\n if (node.kind === 'condition') return conditionFrom(node);\n const items: (FilterNode | null)[] = node.items.map(fromDraftNode);\n const grouped: FilterNode | null = node.op === 'and' ? and(...items) : or(...items);\n return grouped && node.negated ? not(grouped) : grouped;\n}\n\n/** Filter for the draft: incomplete conditions are dropped and the result is canonical. */\nexport function fromDraft(draft: DraftNode, columns: readonly ColumnDef<unknown>[]): FilterNode | null {\n return canonicalizeFilter(fromDraftNode(draft), columns);\n}\n\n/** Replaces the node at path; paths that do not point into groups leave the tree unchanged. */\nfunction replaceAt(draft: DraftNode, path: FilterPath, replace: (node: DraftNode) => DraftNode | null): DraftNode {\n if (path.length === 0) return replace(draft) ?? emptyGroup();\n if (draft.kind !== 'group') return draft;\n const [index, ...rest] = path as [number, ...number[]];\n const child: DraftNode | undefined = draft.items[index];\n if (!child) return draft;\n if (rest.length === 0) {\n const next: DraftNode | null = replace(child);\n if (next === child) return draft;\n const items: DraftNode[] = [...draft.items];\n if (next === null) items.splice(index, 1);\n else items[index] = next;\n return { ...draft, items };\n }\n const next: DraftNode = replaceAt(child, rest, replace);\n if (next === child) return draft;\n const items: DraftNode[] = [...draft.items];\n items[index] = next;\n return { ...draft, items };\n}\n\nexport function addCondition(draft: DraftNode, path: FilterPath): DraftNode {\n return replaceAt(draft, path, node =>\n node.kind === 'group' ? { ...node, items: [...node.items, { kind: 'condition', field: null, op: null }] } : node,\n );\n}\n\n/** Adds an empty group; a group already at MAX_DEPTH returns the tree unchanged. */\nexport function addGroup(draft: DraftNode, path: FilterPath): DraftNode {\n if (path.length + 1 >= MAX_DEPTH) return draft;\n return replaceAt(draft, path, node => (node.kind === 'group' ? { ...node, items: [...node.items, emptyGroup()] } : node));\n}\n\nexport function removeNode(draft: DraftNode, path: FilterPath): DraftNode {\n return replaceAt(draft, path, () => null);\n}\n\n/**\n * Patches a condition or a group. A new field resets the operator to the column's first one\n * (null without columns) and clears the value; an operator of another arity clears the value.\n */\nexport function updateNode(\n draft: DraftNode,\n path: FilterPath,\n patch: Partial<DraftCondition> | GroupPatch,\n columns: readonly ColumnDef<unknown>[] = [],\n): DraftNode {\n return replaceAt(draft, path, node => {\n if (node.kind === 'group') {\n const groupPatch: GroupPatch = patch as GroupPatch;\n return { ...node, ...(groupPatch.op ? { op: groupPatch.op } : {}), ...(groupPatch.negated !== undefined ? { negated: groupPatch.negated } : {}) };\n }\n const conditionPatch: Partial<DraftCondition> = patch as Partial<DraftCondition>;\n if (conditionPatch.field !== undefined && conditionPatch.field !== node.field) {\n const column: ColumnDef<unknown> | undefined = columns.find(c => c.field === conditionPatch.field);\n return { kind: 'condition', field: conditionPatch.field, op: column ? (operatorsFor(column)[0] ?? null) : null };\n }\n const op: ConditionOp | null = conditionPatch.op !== undefined ? conditionPatch.op : node.op;\n const keepValue: boolean = arityOf(op) === arityOf(node.op);\n const value: unknown = 'value' in conditionPatch ? conditionPatch.value : keepValue ? node.value : undefined;\n return value === undefined ? { kind: 'condition', field: node.field, op } : { kind: 'condition', field: node.field, op, value };\n });\n}\n","import { canonicalizeFilter } from '@rastr/core';\nimport type { ColumnDef, ConditionOp, FilterNode, LookupItem } from '@rastr/core';\nimport { closeControlPopover, createDateInput, createSelect } from '@rastr/controls';\nimport type { DateInputControl, SelectControl } from '@rastr/controls';\nimport { h } from './dom';\nimport { resolveFilterBuilderMessages } from './i18n';\nimport type { FilterBuilderMessages } from './i18n';\nimport { addCondition, addGroup, arityOf, fromDraft, MAX_DEPTH, operatorsFor, removeNode, toDraft, updateNode } from './tree';\nimport type { DraftCondition, DraftGroup, DraftNode, FilterPath } from './tree';\n\nexport const TEXT_DEBOUNCE_MS: number = 300;\n\nexport interface FilterBuilderOptions {\n readonly columns: readonly ColumnDef<unknown>[];\n readonly value: FilterNode | null;\n readonly onChange: (value: FilterNode | null) => void;\n readonly locale?: string;\n readonly messages?: Partial<FilterBuilderMessages>;\n /** Distinct values for in/notIn on columns without a lookup, typically store.loadDistinct. */\n readonly loadValues?: (field: string, search: string) => Promise<readonly unknown[]>;\n}\n\nexport interface FilterBuilder {\n setValue(value: FilterNode | null): void;\n /** Reports a typed value still waiting for the debounce (call before using the value, e.g. on save). */\n flush(): void;\n /** Flushes a pending value, then removes the builder. */\n destroy(): void;\n}\n\ntype ConditionDraft = { readonly kind: 'condition' } & DraftCondition;\ntype ValueKind = 'text' | 'number' | 'date' | 'datetime' | 'boolean' | 'lookup';\n\nconst pathKey = (path: FilterPath): string => path.join('.');\n\nfunction valueKind(column: ColumnDef<unknown> | undefined): ValueKind {\n if (!column) return 'text';\n if (column.lookup) return 'lookup';\n const dataType: string = column.dataType ?? 'string';\n if (dataType === 'number' || dataType === 'date' || dataType === 'datetime' || dataType === 'boolean') return dataType;\n return 'text';\n}\n\n/** \"1 234,5\" → 1234.5; empty or unparsable text is no value. */\nfunction parseNumber(text: string): number | undefined {\n const trimmed: string = text.trim();\n if (trimmed === '') return undefined;\n const parsed: number = Number(trimmed.replace(/\\s/g, '').replace(',', '.'));\n return Number.isNaN(parsed) ? undefined : parsed;\n}\n\nfunction formatInput(value: unknown, kind: ValueKind): string {\n if (value === null || value === undefined) return '';\n if (kind === 'number' && typeof value === 'number') return String(value).replace('.', ',');\n return String(value);\n}\n\nfunction parseInput(text: string, kind: ValueKind): unknown {\n if (kind === 'number') return parseNumber(text);\n if (text === '') return undefined;\n return text;\n}\n\nconst sameFilter = (a: FilterNode | null, b: FilterNode | null): boolean => JSON.stringify(a) === JSON.stringify(b);\n\n/** Visual editor of a filter AST: groups with and/or and negation, conditions per column. */\nexport function createFilterBuilder(container: HTMLElement, options: FilterBuilderOptions): FilterBuilder {\n const messages: FilterBuilderMessages = resolveFilterBuilderMessages(options.locale ?? 'cs', options.messages);\n // Columns the grid does not filter by (row numbers, actions, filterable: false) are not offered.\n const columns: readonly ColumnDef<unknown>[] = options.columns.filter(c => c.filterable !== false);\n const root: HTMLDivElement = h('div', { class: 'rs-fb', attrs: { role: 'tree', 'aria-label': messages.label } });\n container.append(root);\n // Delete removes the focused condition, except while editing text in an input.\n root.addEventListener('keydown', event => {\n const target: HTMLElement = event.target as HTMLElement;\n if (event.key !== 'Delete' || (target instanceof HTMLInputElement && target.type !== 'checkbox')) return;\n const condition: HTMLElement | null = target.closest('.rs-fb__condition');\n const path: string | null = condition?.getAttribute('data-path') ?? null;\n if (path === null) return;\n event.preventDefault();\n const indexes: number[] = path.split('.').map(Number);\n const parent: string = indexes.slice(0, -1).join('.');\n pendingFocus = `${parent}:add-condition`;\n change(removeNode(draft, indexes));\n });\n let draft: DraftNode = toDraft(canonicalize(options.value));\n let timer: ReturnType<typeof setTimeout> | null = null;\n let destroyed: boolean = false;\n let pendingFocus: string | null = null;\n\n function canonicalize(value: FilterNode | null): FilterNode | null {\n try {\n return canonicalizeFilter(value, columns);\n } catch {\n return value;\n }\n }\n\n function current(): FilterNode | null {\n try {\n return fromDraft(draft, columns);\n } catch {\n return null;\n }\n }\n\n const emit = (): void => {\n if (timer !== null) clearTimeout(timer);\n timer = null;\n options.onChange(current());\n };\n const flush = (): void => {\n if (timer !== null) emit();\n };\n const emitLater = (): void => {\n if (timer !== null) clearTimeout(timer);\n timer = setTimeout(emit, TEXT_DEBOUNCE_MS);\n };\n\n /** Structural change: new tree, re-render, report at once. */\n const change = (next: DraftNode): void => {\n if (next === draft) return;\n draft = next;\n render();\n emit();\n };\n /** Value typed into an input: no re-render, so the input keeps focus and caret. */\n const setValueQuietly = (path: FilterPath, value: unknown, debounce: boolean): void => {\n draft = updateNode(draft, path, { value });\n if (debounce) emitLater();\n else emit();\n };\n\n const select = (\n cls: string,\n label: string,\n focusId: string,\n items: readonly { value: string; text: string }[],\n selected: string,\n onChange: (value: string) => void,\n ): HTMLButtonElement => {\n const control: SelectControl = createSelect({\n options: items.map(item => ({ value: item.value, label: item.text })),\n value: selected,\n label,\n className: `rs-fb__select ${cls}`,\n onChange,\n });\n control.element.setAttribute('data-fb-focus', focusId);\n return control.element;\n };\n const button = (text: string, cls: string, focusId: string, onClick: () => void, label?: string): HTMLButtonElement => {\n const el: HTMLButtonElement = h('button', { class: `rs-fb__btn ${cls}`, attrs: { type: 'button', 'data-fb-focus': focusId, 'aria-label': label } }, text);\n el.addEventListener('click', onClick);\n return el;\n };\n\n function renderInput(kind: ValueKind, value: unknown, focusId: string, label: string, onValue: (v: unknown) => void): HTMLElement {\n if (kind === 'date' || kind === 'datetime') {\n const date: DateInputControl = createDateInput({\n kind,\n value: typeof value === 'string' && value !== '' ? value : null,\n locale: options.locale ?? 'cs',\n label,\n className: 'rs-fb__date',\n onChange: next => {\n onValue(next ?? undefined);\n flush();\n },\n });\n date.input.setAttribute('data-fb-focus', focusId);\n return date.element;\n }\n const input: HTMLInputElement = h('input', {\n class: 'rs-fb__input',\n attrs: { type: 'text', 'aria-label': label, 'data-fb-focus': focusId, inputmode: kind === 'number' ? 'decimal' : null },\n });\n input.value = formatInput(value, kind);\n input.addEventListener('input', () => onValue(parseInput(input.value, kind)));\n // Leaving the input reports the value at once instead of after the debounce.\n input.addEventListener('change', flush);\n return input;\n }\n\n function lookupItems(column: ColumnDef<unknown>): readonly LookupItem[] {\n return column.lookup ?? [];\n }\n\n /** Checkbox list for in/notIn; values come from the lookup or from loadValues with a search box. */\n function renderList(path: FilterPath, column: ColumnDef<unknown>, value: unknown, focusId: string): HTMLElement {\n const selected: unknown[] = Array.isArray(value) ? [...value] : [];\n const wrapper: HTMLDivElement = h('div', { class: 'rs-fb__list-wrapper' });\n const list: HTMLDivElement = h('div', { class: 'rs-fb__list', attrs: { role: 'group', 'aria-label': messages.value } });\n const isSelected = (v: unknown): boolean => selected.some(s => Object.is(s, v));\n const fill = (items: readonly { value: unknown; text: string }[]): void => {\n // Selected values stay visible even when the current search does not return them.\n const missing: { value: unknown; text: string }[] = selected\n .filter(v => v !== null && !items.some(i => Object.is(i.value, v)))\n .map(v => ({ value: v, text: String(v) }));\n list.replaceChildren(\n ...[...missing, ...items, { value: null, text: messages.blanks }].map((item, index) => {\n const box: HTMLInputElement = h('input', { class: 'rs-fb__check', attrs: { type: 'checkbox', 'data-fb-focus': `${focusId}:${index}` } });\n box.checked = isSelected(item.value);\n box.addEventListener('change', () => {\n const at: number = selected.findIndex(s => Object.is(s, item.value));\n if (box.checked && at < 0) selected.push(item.value);\n if (!box.checked && at >= 0) selected.splice(at, 1);\n setValueQuietly(path, [...selected], false);\n });\n return h('label', { class: 'rs-fb__list-item' }, box, item.text);\n }),\n );\n };\n if (column.lookup) {\n fill(lookupItems(column).map(item => ({ value: item.value, text: item.text })));\n } else if (options.loadValues) {\n const load = options.loadValues;\n const search: HTMLInputElement = h('input', { class: 'rs-fb__input rs-fb__search', attrs: { type: 'search', placeholder: messages.search, 'aria-label': messages.search, 'data-fb-focus': `${focusId}:search` } });\n let searchTimer: ReturnType<typeof setTimeout> | null = null;\n let request: number = 0;\n const run = (text: string): void => {\n const id: number = ++request;\n void load(column.field, text).then(values => {\n if (id !== request || destroyed) return;\n fill(values.filter(v => v !== null && v !== undefined).map(v => ({ value: v, text: String(v) })));\n });\n };\n search.addEventListener('input', () => {\n if (searchTimer !== null) clearTimeout(searchTimer);\n searchTimer = setTimeout(() => run(search.value), TEXT_DEBOUNCE_MS);\n });\n wrapper.append(search);\n fill([]);\n run('');\n } else {\n fill([]);\n }\n wrapper.append(list);\n return wrapper;\n }\n\n function renderValue(path: FilterPath, node: ConditionDraft, column: ColumnDef<unknown> | undefined): HTMLElement {\n const cell: HTMLSpanElement = h('span', { class: 'rs-fb__value' });\n const id: string = pathKey(path);\n const kind: ValueKind = valueKind(column);\n const arity = arityOf(node.op);\n if (!column || arity === 'none') return cell;\n if (arity === 'list') {\n cell.append(renderList(path, column, node.value, `${id}:list`));\n return cell;\n }\n if (kind === 'lookup' || kind === 'boolean') {\n const items: readonly LookupItem[] =\n kind === 'boolean' ? [{ value: true, text: messages.yes }, { value: false, text: messages.no }] : lookupItems(column);\n const index: number = items.findIndex(item => Object.is(item.value, node.value));\n const el: HTMLButtonElement = select(\n 'rs-fb__value-select',\n messages.value,\n `${id}:value`,\n [{ value: '', text: messages.choose }, ...items.map((item, i) => ({ value: String(i), text: item.text }))],\n index < 0 ? '' : String(index),\n value => setValueQuietly(path, value === '' ? undefined : items[Number(value)]?.value, false),\n );\n cell.append(el);\n return cell;\n }\n if (arity === 'pair') {\n const pair: unknown[] = Array.isArray(node.value) ? [...node.value] : [undefined, undefined];\n const setAt = (index: number) => (v: unknown): void => {\n pair[index] = v;\n setValueQuietly(path, [...pair], true);\n };\n cell.append(\n renderInput(kind, pair[0], `${id}:from`, messages.from, setAt(0)),\n h('span', { class: 'rs-fb__dash', attrs: { 'aria-hidden': 'true' } }, '–'),\n renderInput(kind, pair[1], `${id}:to`, messages.to, setAt(1)),\n );\n return cell;\n }\n cell.append(renderInput(kind, node.value, `${id}:value`, messages.value, v => setValueQuietly(path, v, true)));\n return cell;\n }\n\n function renderCondition(path: FilterPath, node: ConditionDraft, level: number): HTMLElement {\n const id: string = pathKey(path);\n const column: ColumnDef<unknown> | undefined = columns.find(c => c.field === node.field);\n const field: HTMLButtonElement = select(\n 'rs-fb__field',\n messages.field,\n `${id}:field`,\n [{ value: '', text: messages.choose }, ...columns.map(c => ({ value: c.field, text: c.caption ?? c.field }))],\n node.field ?? '',\n value => change(updateNode(draft, path, { field: value === '' ? null : value }, columns)),\n );\n const ops: readonly ConditionOp[] = column ? operatorsFor(column) : [];\n const operator: HTMLButtonElement = select(\n 'rs-fb__operator',\n messages.operator,\n `${id}:op`,\n ops.map(op => ({ value: op, text: messages.ops[op] })),\n node.op ?? '',\n value => change(updateNode(draft, path, { op: value as ConditionOp })),\n );\n operator.disabled = !column;\n return h(\n 'div',\n { class: 'rs-fb__condition', attrs: { role: 'treeitem', 'aria-level': level, 'data-path': id } },\n field,\n operator,\n renderValue(path, node, column),\n button('×', 'rs-fb__remove', `${id}:remove`, () => change(removeNode(draft, path)), messages.remove),\n );\n }\n\n function renderGroup(path: FilterPath, node: DraftGroup, level: number): HTMLElement {\n const id: string = pathKey(path);\n const labels: Record<'and' | 'or', string> = node.negated ? { and: messages.nand, or: messages.nor } : { and: messages.and, or: messages.or };\n const ops: HTMLDivElement = h('div', { class: 'rs-fb__ops', attrs: { role: 'group' } });\n for (const op of ['and', 'or'] as const) {\n const el: HTMLButtonElement = button(labels[op], 'rs-fb__op', `${id}:${op}`, () => change(updateNode(draft, path, { op })));\n el.setAttribute('aria-pressed', String(node.op === op));\n ops.append(el);\n }\n const negate: HTMLInputElement = h('input', { class: 'rs-fb__check', attrs: { type: 'checkbox', 'data-fb-focus': `${id}:negate` } });\n negate.checked = node.negated;\n negate.addEventListener('change', () => change(updateNode(draft, path, { negated: negate.checked })));\n const head: HTMLDivElement = h(\n 'div',\n { class: 'rs-fb__group-head' },\n ops,\n h('label', { class: 'rs-fb__negate' }, negate, messages.negate),\n button(messages.addCondition, 'rs-fb__add', `${id}:add-condition`, () => change(addCondition(draft, path))),\n level < MAX_DEPTH ? button(messages.addGroup, 'rs-fb__add', `${id}:add-group`, () => change(addGroup(draft, path))) : null,\n path.length > 0 ? button('×', 'rs-fb__remove', `${id}:remove`, () => change(removeNode(draft, path)), messages.remove) : null,\n );\n const items: HTMLDivElement = h('div', { class: 'rs-fb__items', attrs: { role: 'group' } });\n node.items.forEach((child, index) => {\n const childPath: FilterPath = [...path, index];\n items.append(child.kind === 'group' ? renderGroup(childPath, child, level + 1) : renderCondition(childPath, child, level + 1));\n });\n if (node.items.length === 0) items.append(h('div', { class: 'rs-fb__empty' }, messages.empty));\n return h(\n 'div',\n { class: 'rs-fb__group', attrs: { role: 'treeitem', 'aria-level': level, 'aria-expanded': 'true', 'data-path': id, 'data-negated': node.negated } },\n head,\n items,\n );\n }\n\n function render(): void {\n // Re-rendering replaces the controls; focus returns to the control with the same id.\n const active: string | null = (document.activeElement as HTMLElement | null)?.closest?.('.rs-fb') === root\n ? (document.activeElement?.getAttribute('data-fb-focus') ?? null)\n : null;\n const focused: string | null = pendingFocus ?? active;\n pendingFocus = null;\n closeControlPopover(root);\n root.replaceChildren(draft.kind === 'group' ? renderGroup([], draft, 1) : h('div'));\n if (focused) [...root.querySelectorAll<HTMLElement>('[data-fb-focus]')].find(el => el.getAttribute('data-fb-focus') === focused)?.focus();\n }\n\n render();\n\n return {\n setValue(value: FilterNode | null): void {\n const next: FilterNode | null = canonicalize(value);\n // The same filter keeps the draft, including conditions that are still being filled in.\n if (sameFilter(next, current())) return;\n draft = toDraft(next);\n render();\n },\n flush,\n destroy(): void {\n flush();\n destroyed = true;\n closeControlPopover(root);\n root.remove();\n },\n };\n}\n","type AttrValue = string | number | boolean | null | undefined;\ntype Child = Node | string | null | undefined | false;\n\n/** Creates an element; strings are text nodes, markup is never parsed. */\nexport function h<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n props: { readonly class?: string; readonly attrs?: Readonly<Record<string, AttrValue>> } = {},\n ...children: Child[]\n): HTMLElementTagNameMap[K] {\n const el: HTMLElementTagNameMap[K] = document.createElement(tag);\n if (props.class) el.className = props.class;\n for (const [name, value] of Object.entries(props.attrs ?? {})) {\n if (value === false || value === null || value === undefined) continue;\n el.setAttribute(name, value === true ? '' : String(value));\n }\n for (const child of children) {\n if (child !== null && child !== undefined && child !== false) el.append(child);\n }\n return el;\n}\n","import type { ConditionOp } from '@rastr/core';\n\nexport interface FilterBuilderMessages {\n /** Accessible name of the tree. */\n readonly label: string;\n readonly and: string;\n readonly or: string;\n /** Negated \"and\" group (NAND). */\n readonly nand: string;\n /** Negated \"or\" group (NOR). */\n readonly nor: string;\n readonly negate: string;\n readonly addCondition: string;\n readonly addGroup: string;\n readonly remove: string;\n readonly field: string;\n readonly operator: string;\n readonly value: string;\n readonly from: string;\n readonly to: string;\n readonly choose: string;\n readonly yes: string;\n readonly no: string;\n readonly blanks: string;\n readonly search: string;\n readonly empty: string;\n readonly ops: Readonly<Record<ConditionOp, string>>;\n}\n\nconst cs: FilterBuilderMessages = {\n label: 'Filtr',\n and: 'A',\n or: 'Nebo',\n nand: 'Ne a',\n nor: 'Ne nebo',\n negate: 'Negovat',\n addCondition: 'Přidat podmínku',\n addGroup: 'Přidat skupinu',\n remove: 'Odebrat',\n field: 'Pole',\n operator: 'Operátor',\n value: 'Hodnota',\n from: 'Od',\n to: 'Do',\n choose: '(vyberte)',\n yes: 'Ano',\n no: 'Ne',\n blanks: '(Prázdné)',\n search: 'Hledat…',\n empty: 'Žádné podmínky',\n ops: {\n eq: 'Rovná se',\n neq: 'Nerovná se',\n lt: 'Menší než',\n lte: 'Menší nebo rovno',\n gt: 'Větší než',\n gte: 'Větší nebo rovno',\n between: 'Mezi',\n in: 'Je jedna z',\n notIn: 'Není žádná z',\n contains: 'Obsahuje',\n notContains: 'Neobsahuje',\n startsWith: 'Začíná na',\n endsWith: 'Končí na',\n isNull: 'Je prázdné',\n isNotNull: 'Není prázdné',\n },\n};\n\nconst en: FilterBuilderMessages = {\n label: 'Filter',\n and: 'And',\n or: 'Or',\n nand: 'Not and',\n nor: 'Not or',\n negate: 'Negate',\n addCondition: 'Add condition',\n addGroup: 'Add group',\n remove: 'Remove',\n field: 'Field',\n operator: 'Operator',\n value: 'Value',\n from: 'From',\n to: 'To',\n choose: '(choose)',\n yes: 'Yes',\n no: 'No',\n blanks: '(Blanks)',\n search: 'Search…',\n empty: 'No conditions',\n ops: {\n eq: 'Equals',\n neq: 'Does not equal',\n lt: 'Less than',\n lte: 'Less than or equal',\n gt: 'Greater than',\n gte: 'Greater than or equal',\n between: 'Between',\n in: 'Is any of',\n notIn: 'Is none of',\n contains: 'Contains',\n notContains: 'Does not contain',\n startsWith: 'Starts with',\n endsWith: 'Ends with',\n isNull: 'Is blank',\n isNotNull: 'Is not blank',\n },\n};\n\nexport const FILTER_BUILDER_MESSAGES: Readonly<Record<'cs' | 'en', FilterBuilderMessages>> = { cs, en };\n\nexport function resolveFilterBuilderMessages(locale: string, overrides?: Partial<FilterBuilderMessages>): FilterBuilderMessages {\n const language: string = locale.toLowerCase().split('-')[0] ?? 'cs';\n return { ...(language === 'en' ? en : cs), ...overrides };\n}\n"],"mappings":"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","names":["next","items","import_core"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/tree.ts","../src/builder.ts","../src/dom.ts","../src/i18n.ts"],"sourcesContent":["export { addCondition, addGroup, fromDraft, MAX_DEPTH, operatorsFor, removeNode, toDraft, updateNode } from './tree';\nexport type { DraftCondition, DraftGroup, DraftNode, FilterPath, GroupPatch } from './tree';\nexport { createFilterBuilder, TEXT_DEBOUNCE_MS } from './builder';\nexport type { FilterBuilder, FilterBuilderOptions } from './builder';\nexport { FILTER_BUILDER_MESSAGES, resolveFilterBuilderMessages } from './i18n';\nexport type { FilterBuilderMessages } from './i18n';\n","import { and, canonicalizeFilter, cond, isCondition, isGroup, isNot, normalizeFilter, not, or } from '@rastr/core';\nimport type { ColumnDef, ConditionNode, ConditionOp, DataType, FilterNode } from '@rastr/core';\n\n/** Indexes from the root group to a node. */\nexport type FilterPath = readonly number[];\n\nexport interface DraftCondition {\n readonly field: string | null;\n readonly op: ConditionOp | null;\n readonly value?: unknown;\n}\n\nexport interface DraftGroup {\n readonly kind: 'group';\n readonly op: 'and' | 'or';\n readonly negated: boolean;\n readonly items: readonly DraftNode[];\n}\n\nexport type DraftNode = DraftGroup | ({ readonly kind: 'condition' } & DraftCondition);\n\nexport type GroupPatch = { readonly op?: 'and' | 'or'; readonly negated?: boolean };\n\n/** Groups can be nested up to this many levels including the root. */\nexport const MAX_DEPTH: number = 4;\n\nconst TEXT_OPS: readonly ConditionOp[] = ['contains', 'notContains', 'startsWith', 'endsWith', 'eq', 'neq'];\nconst RANGE_OPS: readonly ConditionOp[] = ['eq', 'neq', 'lt', 'lte', 'gt', 'gte', 'between'];\nconst LIST_OPS: readonly ConditionOp[] = ['in', 'notIn'];\nconst NULL_OPS: readonly ConditionOp[] = ['isNull', 'isNotNull'];\n\n/** Operators offered for a column: the filter row's list plus in/notIn and null tests. */\nexport function operatorsFor(column: ColumnDef<unknown>): readonly ConditionOp[] {\n if (column.lookup) return ['eq', 'neq', ...LIST_OPS, ...NULL_OPS];\n const dataType: DataType = column.dataType ?? 'string';\n if (dataType === 'boolean') return ['eq', ...NULL_OPS];\n if (dataType === 'number' || dataType === 'date' || dataType === 'datetime') return [...RANGE_OPS, ...LIST_OPS, ...NULL_OPS];\n return [...TEXT_OPS, ...LIST_OPS, ...NULL_OPS];\n}\n\n/** How many values an operator takes; a changed arity clears the value. */\nexport function arityOf(op: ConditionOp | null): 'none' | 'single' | 'pair' | 'list' {\n if (op === null || op === 'isNull' || op === 'isNotNull') return 'none';\n if (op === 'between') return 'pair';\n if (op === 'in' || op === 'notIn') return 'list';\n return 'single';\n}\n\nconst emptyGroup = (op: 'and' | 'or' = 'and', negated: boolean = false, items: readonly DraftNode[] = []): DraftGroup => ({\n kind: 'group',\n op,\n negated,\n items,\n});\n\nconst draftCondition = (node: ConditionNode): DraftNode =>\n node.value === undefined ? { kind: 'condition', field: node.field, op: node.op } : { kind: 'condition', field: node.field, op: node.op, value: node.value };\n\n/** or(in, isNull) on one field, as written by the header filter with \"(Blanks)\" selected. */\nfunction foldBlanks(items: readonly FilterNode[]): ConditionNode | null {\n if (items.length !== 2 || !items.every(isCondition)) return null;\n const [first, second] = items as readonly ConditionNode[];\n const list: ConditionNode | undefined = [first, second].find(c => c?.op === 'in');\n const blanks: ConditionNode | undefined = [first, second].find(c => c?.op === 'isNull');\n if (!list || !blanks || list.field !== blanks.field || !Array.isArray(list.value)) return null;\n return cond(list.field, 'in', [...(list.value as unknown[]), null]);\n}\n\nfunction toDraftNode(node: FilterNode): DraftNode {\n if (isNot(node)) {\n const inner: DraftNode = toDraftNode(node.item);\n return inner.kind === 'group' ? { ...inner, negated: !inner.negated } : emptyGroup('and', true, [inner]);\n }\n if (isGroup(node)) {\n const folded: ConditionNode | null = node.op === 'or' ? foldBlanks(node.items) : null;\n if (folded) return draftCondition(folded);\n return emptyGroup(node.op, false, node.items.map(toDraftNode));\n }\n return draftCondition(node);\n}\n\n/** Editable tree for a filter; the root is always a group. */\nexport function toDraft(filter: FilterNode | null): DraftNode {\n const normalized: FilterNode | null = normalizeFilter(filter);\n if (normalized === null) return emptyGroup();\n const draft: DraftNode = toDraftNode(normalized);\n return draft.kind === 'group' ? draft : emptyGroup('and', false, [draft]);\n}\n\nconst isBlankValue = (value: unknown): boolean => value === undefined || value === null || value === '';\n\nfunction conditionFrom(node: DraftCondition): FilterNode | null {\n if (node.field === null || node.op === null) return null;\n const { field, op, value } = node;\n switch (arityOf(op)) {\n case 'none':\n return cond(field, op);\n case 'single':\n return isBlankValue(value) ? null : cond(field, op, value);\n case 'pair': {\n if (!Array.isArray(value) || value.length !== 2 || value.some(isBlankValue)) return null;\n return cond(field, op, value);\n }\n case 'list': {\n if (!Array.isArray(value)) return null;\n const values: unknown[] = value.filter(v => v !== null && v !== undefined);\n const blanks: boolean = values.length !== value.length;\n const list: FilterNode | null = values.length > 0 ? cond(field, op, values) : null;\n if (!blanks) return list;\n // A blanks entry is the null test the header filter writes next to the list.\n return op === 'in' ? or(list, cond(field, 'isNull')) : and(list, cond(field, 'isNotNull'));\n }\n }\n}\n\nfunction fromDraftNode(node: DraftNode): FilterNode | null {\n if (node.kind === 'condition') return conditionFrom(node);\n const items: (FilterNode | null)[] = node.items.map(fromDraftNode);\n const grouped: FilterNode | null = node.op === 'and' ? and(...items) : or(...items);\n return grouped && node.negated ? not(grouped) : grouped;\n}\n\n/** Filter for the draft: incomplete conditions are dropped and the result is canonical. */\nexport function fromDraft(draft: DraftNode, columns: readonly ColumnDef<unknown>[]): FilterNode | null {\n return canonicalizeFilter(fromDraftNode(draft), columns);\n}\n\n/** Replaces the node at path; paths that do not point into groups leave the tree unchanged. */\nfunction replaceAt(draft: DraftNode, path: FilterPath, replace: (node: DraftNode) => DraftNode | null): DraftNode {\n if (path.length === 0) return replace(draft) ?? emptyGroup();\n if (draft.kind !== 'group') return draft;\n const [index, ...rest] = path as [number, ...number[]];\n const child: DraftNode | undefined = draft.items[index];\n if (!child) return draft;\n if (rest.length === 0) {\n const next: DraftNode | null = replace(child);\n if (next === child) return draft;\n const items: DraftNode[] = [...draft.items];\n if (next === null) items.splice(index, 1);\n else items[index] = next;\n return { ...draft, items };\n }\n const next: DraftNode = replaceAt(child, rest, replace);\n if (next === child) return draft;\n const items: DraftNode[] = [...draft.items];\n items[index] = next;\n return { ...draft, items };\n}\n\nexport function addCondition(draft: DraftNode, path: FilterPath): DraftNode {\n return replaceAt(draft, path, node =>\n node.kind === 'group' ? { ...node, items: [...node.items, { kind: 'condition', field: null, op: null }] } : node,\n );\n}\n\n/** Adds an empty group; a group already at MAX_DEPTH returns the tree unchanged. */\nexport function addGroup(draft: DraftNode, path: FilterPath): DraftNode {\n if (path.length + 1 >= MAX_DEPTH) return draft;\n return replaceAt(draft, path, node => (node.kind === 'group' ? { ...node, items: [...node.items, emptyGroup()] } : node));\n}\n\nexport function removeNode(draft: DraftNode, path: FilterPath): DraftNode {\n return replaceAt(draft, path, () => null);\n}\n\n/**\n * Patches a condition or a group. A new field resets the operator to the column's first one\n * (null without columns) and clears the value; an operator of another arity clears the value.\n */\nexport function updateNode(\n draft: DraftNode,\n path: FilterPath,\n patch: Partial<DraftCondition> | GroupPatch,\n columns: readonly ColumnDef<unknown>[] = [],\n): DraftNode {\n return replaceAt(draft, path, node => {\n if (node.kind === 'group') {\n const groupPatch: GroupPatch = patch as GroupPatch;\n return { ...node, ...(groupPatch.op ? { op: groupPatch.op } : {}), ...(groupPatch.negated !== undefined ? { negated: groupPatch.negated } : {}) };\n }\n const conditionPatch: Partial<DraftCondition> = patch as Partial<DraftCondition>;\n if (conditionPatch.field !== undefined && conditionPatch.field !== node.field) {\n const column: ColumnDef<unknown> | undefined = columns.find(c => c.field === conditionPatch.field);\n return { kind: 'condition', field: conditionPatch.field, op: column ? (operatorsFor(column)[0] ?? null) : null };\n }\n const op: ConditionOp | null = conditionPatch.op !== undefined ? conditionPatch.op : node.op;\n const keepValue: boolean = arityOf(op) === arityOf(node.op);\n const value: unknown = 'value' in conditionPatch ? conditionPatch.value : keepValue ? node.value : undefined;\n return value === undefined ? { kind: 'condition', field: node.field, op } : { kind: 'condition', field: node.field, op, value };\n });\n}\n","import { canonicalizeFilter } from '@rastr/core';\nimport type { ColumnDef, ConditionOp, FilterNode, LookupItem } from '@rastr/core';\nimport { closeControlPopover, createDateInput, createSelect } from '@rastr/controls';\nimport type { DateInputControl, SelectControl } from '@rastr/controls';\nimport { h } from './dom';\nimport { resolveFilterBuilderMessages } from './i18n';\nimport type { FilterBuilderMessages } from './i18n';\nimport { addCondition, addGroup, arityOf, fromDraft, MAX_DEPTH, operatorsFor, removeNode, toDraft, updateNode } from './tree';\nimport type { DraftCondition, DraftGroup, DraftNode, FilterPath } from './tree';\n\nexport const TEXT_DEBOUNCE_MS: number = 300;\n\nexport interface FilterBuilderOptions {\n readonly columns: readonly ColumnDef<unknown>[];\n readonly value: FilterNode | null;\n readonly onChange: (value: FilterNode | null) => void;\n readonly locale?: string;\n readonly messages?: Partial<FilterBuilderMessages>;\n /** Distinct values for in/notIn on columns without a lookup, typically store.loadDistinct. */\n readonly loadValues?: (field: string, search: string) => Promise<readonly unknown[]>;\n}\n\nexport interface FilterBuilder {\n setValue(value: FilterNode | null): void;\n /** Reports a typed value still waiting for the debounce (call before using the value, e.g. on save). */\n flush(): void;\n /** Flushes a pending value, then removes the builder. */\n destroy(): void;\n}\n\ntype ConditionDraft = { readonly kind: 'condition' } & DraftCondition;\ntype ValueKind = 'text' | 'number' | 'date' | 'datetime' | 'boolean' | 'lookup';\n\nconst pathKey = (path: FilterPath): string => path.join('.');\n\nfunction valueKind(column: ColumnDef<unknown> | undefined): ValueKind {\n if (!column) return 'text';\n if (column.lookup) return 'lookup';\n const dataType: string = column.dataType ?? 'string';\n if (dataType === 'number' || dataType === 'date' || dataType === 'datetime' || dataType === 'boolean') return dataType;\n return 'text';\n}\n\n/** \"1 234,5\" → 1234.5; empty or unparsable text is no value. */\nfunction parseNumber(text: string): number | undefined {\n const trimmed: string = text.trim();\n if (trimmed === '') return undefined;\n const parsed: number = Number(trimmed.replace(/\\s/g, '').replace(',', '.'));\n return Number.isNaN(parsed) ? undefined : parsed;\n}\n\nfunction formatInput(value: unknown, kind: ValueKind): string {\n if (value === null || value === undefined) return '';\n if (kind === 'number' && typeof value === 'number') return String(value).replace('.', ',');\n return String(value);\n}\n\nfunction parseInput(text: string, kind: ValueKind): unknown {\n if (kind === 'number') return parseNumber(text);\n if (text === '') return undefined;\n return text;\n}\n\nconst sameFilter = (a: FilterNode | null, b: FilterNode | null): boolean => JSON.stringify(a) === JSON.stringify(b);\n\n/** Visual editor of a filter AST: groups with and/or and negation, conditions per column. */\nexport function createFilterBuilder(container: HTMLElement, options: FilterBuilderOptions): FilterBuilder {\n const messages: FilterBuilderMessages = resolveFilterBuilderMessages(options.locale ?? 'cs', options.messages);\n // Columns the grid does not filter by (row numbers, actions, filterable: false) are not offered.\n const columns: readonly ColumnDef<unknown>[] = options.columns.filter(c => c.filterable !== false);\n const root: HTMLDivElement = h('div', { class: 'rs-fb', attrs: { role: 'tree', 'aria-label': messages.label } });\n container.append(root);\n // Delete removes the focused condition, except while editing text in an input.\n root.addEventListener('keydown', event => {\n const target: HTMLElement = event.target as HTMLElement;\n if (event.key !== 'Delete' || (target instanceof HTMLInputElement && target.type !== 'checkbox')) return;\n const condition: HTMLElement | null = target.closest('.rs-fb__condition');\n const path: string | null = condition?.getAttribute('data-path') ?? null;\n if (path === null) return;\n event.preventDefault();\n const indexes: number[] = path.split('.').map(Number);\n const parent: string = indexes.slice(0, -1).join('.');\n pendingFocus = `${parent}:add-condition`;\n change(removeNode(draft, indexes));\n });\n let draft: DraftNode = toDraft(canonicalize(options.value));\n let timer: ReturnType<typeof setTimeout> | null = null;\n let destroyed: boolean = false;\n let pendingFocus: string | null = null;\n\n function canonicalize(value: FilterNode | null): FilterNode | null {\n try {\n return canonicalizeFilter(value, columns);\n } catch {\n return value;\n }\n }\n\n function current(): FilterNode | null {\n try {\n return fromDraft(draft, columns);\n } catch {\n return null;\n }\n }\n\n const emit = (): void => {\n if (timer !== null) clearTimeout(timer);\n timer = null;\n options.onChange(current());\n };\n const flush = (): void => {\n if (timer !== null) emit();\n };\n const emitLater = (): void => {\n if (timer !== null) clearTimeout(timer);\n timer = setTimeout(emit, TEXT_DEBOUNCE_MS);\n };\n\n /** Structural change: new tree, re-render, report at once. */\n const change = (next: DraftNode): void => {\n if (next === draft) return;\n draft = next;\n render();\n emit();\n };\n /** Value typed into an input: no re-render, so the input keeps focus and caret. */\n const setValueQuietly = (path: FilterPath, value: unknown, debounce: boolean): void => {\n draft = updateNode(draft, path, { value });\n if (debounce) emitLater();\n else emit();\n };\n\n const select = (\n cls: string,\n label: string,\n focusId: string,\n items: readonly { value: string; text: string }[],\n selected: string,\n onChange: (value: string) => void,\n ): HTMLButtonElement => {\n const control: SelectControl = createSelect({\n options: items.map(item => ({ value: item.value, label: item.text })),\n value: selected,\n label,\n className: `rs-fb__select ${cls}`,\n onChange,\n });\n control.element.setAttribute('data-fb-focus', focusId);\n return control.element;\n };\n const button = (text: string, cls: string, focusId: string, onClick: () => void, label?: string): HTMLButtonElement => {\n const el: HTMLButtonElement = h('button', { class: `rs-fb__btn ${cls}`, attrs: { type: 'button', 'data-fb-focus': focusId, 'aria-label': label } }, text);\n el.addEventListener('click', onClick);\n return el;\n };\n\n function renderInput(kind: ValueKind, value: unknown, focusId: string, label: string, onValue: (v: unknown) => void): HTMLElement {\n if (kind === 'date' || kind === 'datetime') {\n const date: DateInputControl = createDateInput({\n kind,\n value: typeof value === 'string' && value !== '' ? value : null,\n locale: options.locale ?? 'cs',\n label,\n className: 'rs-fb__date',\n onChange: next => {\n onValue(next ?? undefined);\n flush();\n },\n });\n date.input.setAttribute('data-fb-focus', focusId);\n return date.element;\n }\n const input: HTMLInputElement = h('input', {\n class: 'rs-fb__input',\n attrs: { type: 'text', 'aria-label': label, 'data-fb-focus': focusId, inputmode: kind === 'number' ? 'decimal' : null },\n });\n input.value = formatInput(value, kind);\n input.addEventListener('input', () => onValue(parseInput(input.value, kind)));\n // Leaving the input reports the value at once instead of after the debounce.\n input.addEventListener('change', flush);\n return input;\n }\n\n function lookupItems(column: ColumnDef<unknown>): readonly LookupItem[] {\n return column.lookup ?? [];\n }\n\n /** Checkbox list for in/notIn; values come from the lookup or from loadValues with a search box. */\n function renderList(path: FilterPath, column: ColumnDef<unknown>, value: unknown, focusId: string): HTMLElement {\n const selected: unknown[] = Array.isArray(value) ? [...value] : [];\n const wrapper: HTMLDivElement = h('div', { class: 'rs-fb__list-wrapper' });\n const list: HTMLDivElement = h('div', { class: 'rs-fb__list', attrs: { role: 'group', 'aria-label': messages.value } });\n const isSelected = (v: unknown): boolean => selected.some(s => Object.is(s, v));\n const fill = (items: readonly { value: unknown; text: string }[]): void => {\n // Selected values stay visible even when the current search does not return them.\n const missing: { value: unknown; text: string }[] = selected\n .filter(v => v !== null && !items.some(i => Object.is(i.value, v)))\n .map(v => ({ value: v, text: String(v) }));\n list.replaceChildren(\n ...[...missing, ...items, { value: null, text: messages.blanks }].map((item, index) => {\n const box: HTMLInputElement = h('input', { class: 'rs-fb__check', attrs: { type: 'checkbox', 'data-fb-focus': `${focusId}:${index}` } });\n box.checked = isSelected(item.value);\n box.addEventListener('change', () => {\n const at: number = selected.findIndex(s => Object.is(s, item.value));\n if (box.checked && at < 0) selected.push(item.value);\n if (!box.checked && at >= 0) selected.splice(at, 1);\n setValueQuietly(path, [...selected], false);\n });\n return h('label', { class: 'rs-fb__list-item' }, box, item.text);\n }),\n );\n };\n if (column.lookup) {\n fill(lookupItems(column).map(item => ({ value: item.value, text: item.text })));\n } else if (options.loadValues) {\n const load = options.loadValues;\n const search: HTMLInputElement = h('input', { class: 'rs-fb__input rs-fb__search', attrs: { type: 'search', placeholder: messages.search, 'aria-label': messages.search, 'data-fb-focus': `${focusId}:search` } });\n let searchTimer: ReturnType<typeof setTimeout> | null = null;\n let request: number = 0;\n const run = (text: string): void => {\n const id: number = ++request;\n void load(column.field, text).then(values => {\n if (id !== request || destroyed) return;\n fill(values.filter(v => v !== null && v !== undefined).map(v => ({ value: v, text: String(v) })));\n });\n };\n search.addEventListener('input', () => {\n if (searchTimer !== null) clearTimeout(searchTimer);\n searchTimer = setTimeout(() => run(search.value), TEXT_DEBOUNCE_MS);\n });\n wrapper.append(search);\n fill([]);\n run('');\n } else {\n fill([]);\n }\n wrapper.append(list);\n return wrapper;\n }\n\n function renderValue(path: FilterPath, node: ConditionDraft, column: ColumnDef<unknown> | undefined): HTMLElement {\n const cell: HTMLSpanElement = h('span', { class: 'rs-fb__value' });\n const id: string = pathKey(path);\n const kind: ValueKind = valueKind(column);\n const arity = arityOf(node.op);\n if (!column || arity === 'none') return cell;\n if (arity === 'list') {\n cell.append(renderList(path, column, node.value, `${id}:list`));\n return cell;\n }\n if (kind === 'lookup' || kind === 'boolean') {\n const items: readonly LookupItem[] =\n kind === 'boolean' ? [{ value: true, text: messages.yes }, { value: false, text: messages.no }] : lookupItems(column);\n const index: number = items.findIndex(item => Object.is(item.value, node.value));\n const el: HTMLButtonElement = select(\n 'rs-fb__value-select',\n messages.value,\n `${id}:value`,\n [{ value: '', text: messages.choose }, ...items.map((item, i) => ({ value: String(i), text: item.text }))],\n index < 0 ? '' : String(index),\n value => setValueQuietly(path, value === '' ? undefined : items[Number(value)]?.value, false),\n );\n cell.append(el);\n return cell;\n }\n if (arity === 'pair') {\n const pair: unknown[] = Array.isArray(node.value) ? [...node.value] : [undefined, undefined];\n const setAt = (index: number) => (v: unknown): void => {\n pair[index] = v;\n setValueQuietly(path, [...pair], true);\n };\n cell.append(\n renderInput(kind, pair[0], `${id}:from`, messages.from, setAt(0)),\n h('span', { class: 'rs-fb__dash', attrs: { 'aria-hidden': 'true' } }, '–'),\n renderInput(kind, pair[1], `${id}:to`, messages.to, setAt(1)),\n );\n return cell;\n }\n cell.append(renderInput(kind, node.value, `${id}:value`, messages.value, v => setValueQuietly(path, v, true)));\n return cell;\n }\n\n function renderCondition(path: FilterPath, node: ConditionDraft, level: number): HTMLElement {\n const id: string = pathKey(path);\n const column: ColumnDef<unknown> | undefined = columns.find(c => c.field === node.field);\n const field: HTMLButtonElement = select(\n 'rs-fb__field',\n messages.field,\n `${id}:field`,\n [{ value: '', text: messages.choose }, ...columns.map(c => ({ value: c.field, text: c.caption ?? c.field }))],\n node.field ?? '',\n value => change(updateNode(draft, path, { field: value === '' ? null : value }, columns)),\n );\n const ops: readonly ConditionOp[] = column ? operatorsFor(column) : [];\n const operator: HTMLButtonElement = select(\n 'rs-fb__operator',\n messages.operator,\n `${id}:op`,\n ops.map(op => ({ value: op, text: messages.ops[op] })),\n node.op ?? '',\n value => change(updateNode(draft, path, { op: value as ConditionOp })),\n );\n operator.disabled = !column;\n return h(\n 'div',\n { class: 'rs-fb__condition', attrs: { role: 'treeitem', 'aria-level': level, 'data-path': id } },\n field,\n operator,\n renderValue(path, node, column),\n button('×', 'rs-fb__remove', `${id}:remove`, () => change(removeNode(draft, path)), messages.remove),\n );\n }\n\n function renderGroup(path: FilterPath, node: DraftGroup, level: number): HTMLElement {\n const id: string = pathKey(path);\n const labels: Record<'and' | 'or', string> = node.negated ? { and: messages.nand, or: messages.nor } : { and: messages.and, or: messages.or };\n const ops: HTMLDivElement = h('div', { class: 'rs-fb__ops', attrs: { role: 'group' } });\n for (const op of ['and', 'or'] as const) {\n const el: HTMLButtonElement = button(labels[op], 'rs-fb__op', `${id}:${op}`, () => change(updateNode(draft, path, { op })));\n el.setAttribute('aria-pressed', String(node.op === op));\n ops.append(el);\n }\n const negate: HTMLInputElement = h('input', { class: 'rs-fb__check', attrs: { type: 'checkbox', 'data-fb-focus': `${id}:negate` } });\n negate.checked = node.negated;\n negate.addEventListener('change', () => change(updateNode(draft, path, { negated: negate.checked })));\n const head: HTMLDivElement = h(\n 'div',\n { class: 'rs-fb__group-head' },\n ops,\n h('label', { class: 'rs-fb__negate' }, negate, messages.negate),\n button(messages.addCondition, 'rs-fb__add', `${id}:add-condition`, () => change(addCondition(draft, path))),\n level < MAX_DEPTH ? button(messages.addGroup, 'rs-fb__add', `${id}:add-group`, () => change(addGroup(draft, path))) : null,\n path.length > 0 ? button('×', 'rs-fb__remove', `${id}:remove`, () => change(removeNode(draft, path)), messages.remove) : null,\n );\n const items: HTMLDivElement = h('div', { class: 'rs-fb__items', attrs: { role: 'group' } });\n node.items.forEach((child, index) => {\n const childPath: FilterPath = [...path, index];\n items.append(child.kind === 'group' ? renderGroup(childPath, child, level + 1) : renderCondition(childPath, child, level + 1));\n });\n if (node.items.length === 0) items.append(h('div', { class: 'rs-fb__empty' }, messages.empty));\n return h(\n 'div',\n { class: 'rs-fb__group', attrs: { role: 'treeitem', 'aria-level': level, 'aria-expanded': 'true', 'data-path': id, 'data-negated': node.negated } },\n head,\n items,\n );\n }\n\n function render(): void {\n // Re-rendering replaces the controls; focus returns to the control with the same id.\n const active: string | null = (document.activeElement as HTMLElement | null)?.closest?.('.rs-fb') === root\n ? (document.activeElement?.getAttribute('data-fb-focus') ?? null)\n : null;\n const focused: string | null = pendingFocus ?? active;\n pendingFocus = null;\n closeControlPopover(root);\n root.replaceChildren(draft.kind === 'group' ? renderGroup([], draft, 1) : h('div'));\n if (focused) [...root.querySelectorAll<HTMLElement>('[data-fb-focus]')].find(el => el.getAttribute('data-fb-focus') === focused)?.focus();\n }\n\n render();\n\n return {\n setValue(value: FilterNode | null): void {\n const next: FilterNode | null = canonicalize(value);\n // The same filter keeps the draft, including conditions that are still being filled in.\n if (sameFilter(next, current())) return;\n draft = toDraft(next);\n render();\n },\n flush,\n destroy(): void {\n flush();\n destroyed = true;\n closeControlPopover(root);\n root.remove();\n },\n };\n}\n","type AttrValue = string | number | boolean | null | undefined;\ntype Child = Node | string | null | undefined | false;\n\n/** Creates an element; strings are text nodes, markup is never parsed. */\nexport function h<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n props: { readonly class?: string; readonly attrs?: Readonly<Record<string, AttrValue>> } = {},\n ...children: Child[]\n): HTMLElementTagNameMap[K] {\n const el: HTMLElementTagNameMap[K] = document.createElement(tag);\n if (props.class) el.className = props.class;\n for (const [name, value] of Object.entries(props.attrs ?? {})) {\n if (value === false || value === null || value === undefined) continue;\n el.setAttribute(name, value === true ? '' : String(value));\n }\n for (const child of children) {\n if (child !== null && child !== undefined && child !== false) el.append(child);\n }\n return el;\n}\n","import { messageLanguage } from '@rastr/core';\nimport type { ConditionOp, MessageLanguage } from '@rastr/core';\n\nexport interface FilterBuilderMessages {\n /** Accessible name of the tree. */\n readonly label: string;\n readonly and: string;\n readonly or: string;\n /** Negated \"and\" group (NAND). */\n readonly nand: string;\n /** Negated \"or\" group (NOR). */\n readonly nor: string;\n readonly negate: string;\n readonly addCondition: string;\n readonly addGroup: string;\n readonly remove: string;\n readonly field: string;\n readonly operator: string;\n readonly value: string;\n readonly from: string;\n readonly to: string;\n readonly choose: string;\n readonly yes: string;\n readonly no: string;\n readonly blanks: string;\n readonly search: string;\n readonly empty: string;\n readonly ops: Readonly<Record<ConditionOp, string>>;\n}\n\nconst cs: FilterBuilderMessages = {\n label: 'Filtr',\n and: 'A',\n or: 'Nebo',\n nand: 'Ne a',\n nor: 'Ne nebo',\n negate: 'Negovat',\n addCondition: 'Přidat podmínku',\n addGroup: 'Přidat skupinu',\n remove: 'Odebrat',\n field: 'Pole',\n operator: 'Operátor',\n value: 'Hodnota',\n from: 'Od',\n to: 'Do',\n choose: '(vyberte)',\n yes: 'Ano',\n no: 'Ne',\n blanks: '(Prázdné)',\n search: 'Hledat…',\n empty: 'Žádné podmínky',\n ops: {\n eq: 'Rovná se',\n neq: 'Nerovná se',\n lt: 'Menší než',\n lte: 'Menší nebo rovno',\n gt: 'Větší než',\n gte: 'Větší nebo rovno',\n between: 'Mezi',\n in: 'Je jedna z',\n notIn: 'Není žádná z',\n contains: 'Obsahuje',\n notContains: 'Neobsahuje',\n startsWith: 'Začíná na',\n endsWith: 'Končí na',\n isNull: 'Je prázdné',\n isNotNull: 'Není prázdné',\n },\n};\n\nconst en: FilterBuilderMessages = {\n label: 'Filter',\n and: 'And',\n or: 'Or',\n nand: 'Not and',\n nor: 'Not or',\n negate: 'Negate',\n addCondition: 'Add condition',\n addGroup: 'Add group',\n remove: 'Remove',\n field: 'Field',\n operator: 'Operator',\n value: 'Value',\n from: 'From',\n to: 'To',\n choose: '(choose)',\n yes: 'Yes',\n no: 'No',\n blanks: '(Blanks)',\n search: 'Search…',\n empty: 'No conditions',\n ops: {\n eq: 'Equals',\n neq: 'Does not equal',\n lt: 'Less than',\n lte: 'Less than or equal',\n gt: 'Greater than',\n gte: 'Greater than or equal',\n between: 'Between',\n in: 'Is any of',\n notIn: 'Is none of',\n contains: 'Contains',\n notContains: 'Does not contain',\n startsWith: 'Starts with',\n endsWith: 'Ends with',\n isNull: 'Is blank',\n isNotNull: 'Is not blank',\n },\n};\n\nconst de: FilterBuilderMessages = {\n label: 'Filter',\n and: 'Und',\n or: 'Oder',\n nand: 'Nicht und',\n nor: 'Nicht oder',\n negate: 'Negieren',\n addCondition: 'Bedingung hinzufügen',\n addGroup: 'Gruppe hinzufügen',\n remove: 'Entfernen',\n field: 'Feld',\n operator: 'Operator',\n value: 'Wert',\n from: 'Von',\n to: 'Bis',\n choose: '(auswählen)',\n yes: 'Ja',\n no: 'Nein',\n blanks: '(Leer)',\n search: 'Suchen…',\n empty: 'Keine Bedingungen',\n ops: {\n eq: 'Gleich',\n neq: 'Ungleich',\n lt: 'Kleiner als',\n lte: 'Kleiner oder gleich',\n gt: 'Größer als',\n gte: 'Größer oder gleich',\n between: 'Zwischen',\n in: 'Ist einer von',\n notIn: 'Ist keiner von',\n contains: 'Enthält',\n notContains: 'Enthält nicht',\n startsWith: 'Beginnt mit',\n endsWith: 'Endet mit',\n isNull: 'Ist leer',\n isNotNull: 'Ist nicht leer',\n },\n};\n\nconst pl: FilterBuilderMessages = {\n label: 'Filtr',\n and: 'I',\n or: 'Lub',\n nand: 'Nie i',\n nor: 'Nie lub',\n negate: 'Zaneguj',\n addCondition: 'Dodaj warunek',\n addGroup: 'Dodaj grupę',\n remove: 'Usuń',\n field: 'Pole',\n operator: 'Operator',\n value: 'Wartość',\n from: 'Od',\n to: 'Do',\n choose: '(wybierz)',\n yes: 'Tak',\n no: 'Nie',\n blanks: '(Puste)',\n search: 'Szukaj…',\n empty: 'Brak warunków',\n ops: {\n eq: 'Równa się',\n neq: 'Nie równa się',\n lt: 'Mniejsze niż',\n lte: 'Mniejsze lub równe',\n gt: 'Większe niż',\n gte: 'Większe lub równe',\n between: 'Pomiędzy',\n in: 'Jest jednym z',\n notIn: 'Nie jest żadnym z',\n contains: 'Zawiera',\n notContains: 'Nie zawiera',\n startsWith: 'Zaczyna się od',\n endsWith: 'Kończy się na',\n isNull: 'Jest puste',\n isNotNull: 'Nie jest puste',\n },\n};\n\nconst sk: FilterBuilderMessages = {\n label: 'Filter',\n and: 'A',\n or: 'Alebo',\n nand: 'Nie a',\n nor: 'Nie alebo',\n negate: 'Negovať',\n addCondition: 'Pridať podmienku',\n addGroup: 'Pridať skupinu',\n remove: 'Odstrániť',\n field: 'Pole',\n operator: 'Operátor',\n value: 'Hodnota',\n from: 'Od',\n to: 'Do',\n choose: '(vyberte)',\n yes: 'Áno',\n no: 'Nie',\n blanks: '(Prázdne)',\n search: 'Hľadať…',\n empty: 'Žiadne podmienky',\n ops: {\n eq: 'Rovná sa',\n neq: 'Nerovná sa',\n lt: 'Menšie ako',\n lte: 'Menšie alebo rovné',\n gt: 'Väčšie ako',\n gte: 'Väčšie alebo rovné',\n between: 'Medzi',\n in: 'Je jedna z',\n notIn: 'Nie je žiadna z',\n contains: 'Obsahuje',\n notContains: 'Neobsahuje',\n startsWith: 'Začína na',\n endsWith: 'Končí na',\n isNull: 'Je prázdne',\n isNotNull: 'Nie je prázdne',\n },\n};\n\nexport const FILTER_BUILDER_MESSAGES: Readonly<Record<MessageLanguage, FilterBuilderMessages>> = { cs, en, de, pl, sk };\n\n/** Texts for a locale (\"de-CH\" → German; Czech for languages without texts) with overrides. */\nexport function resolveFilterBuilderMessages(locale: string, overrides?: Partial<FilterBuilderMessages>): FilterBuilderMessages {\n return { ...FILTER_BUILDER_MESSAGES[messageLanguage(locale)], ...overrides };\n}\n"],"mappings":"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","names":["next","items","import_core","import_core"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ConditionOp, ColumnDef, FilterNode } from '@rastr/core';
|
|
1
|
+
import { ConditionOp, ColumnDef, FilterNode, MessageLanguage } from '@rastr/core';
|
|
2
2
|
|
|
3
3
|
/** Indexes from the root group to a node. */
|
|
4
4
|
type FilterPath = readonly number[];
|
|
@@ -64,7 +64,8 @@ interface FilterBuilderMessages {
|
|
|
64
64
|
readonly empty: string;
|
|
65
65
|
readonly ops: Readonly<Record<ConditionOp, string>>;
|
|
66
66
|
}
|
|
67
|
-
declare const FILTER_BUILDER_MESSAGES: Readonly<Record<
|
|
67
|
+
declare const FILTER_BUILDER_MESSAGES: Readonly<Record<MessageLanguage, FilterBuilderMessages>>;
|
|
68
|
+
/** Texts for a locale ("de-CH" → German; Czech for languages without texts) with overrides. */
|
|
68
69
|
declare function resolveFilterBuilderMessages(locale: string, overrides?: Partial<FilterBuilderMessages>): FilterBuilderMessages;
|
|
69
70
|
|
|
70
71
|
declare const TEXT_DEBOUNCE_MS: number;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ConditionOp, ColumnDef, FilterNode } from '@rastr/core';
|
|
1
|
+
import { ConditionOp, ColumnDef, FilterNode, MessageLanguage } from '@rastr/core';
|
|
2
2
|
|
|
3
3
|
/** Indexes from the root group to a node. */
|
|
4
4
|
type FilterPath = readonly number[];
|
|
@@ -64,7 +64,8 @@ interface FilterBuilderMessages {
|
|
|
64
64
|
readonly empty: string;
|
|
65
65
|
readonly ops: Readonly<Record<ConditionOp, string>>;
|
|
66
66
|
}
|
|
67
|
-
declare const FILTER_BUILDER_MESSAGES: Readonly<Record<
|
|
67
|
+
declare const FILTER_BUILDER_MESSAGES: Readonly<Record<MessageLanguage, FilterBuilderMessages>>;
|
|
68
|
+
/** Texts for a locale ("de-CH" → German; Czech for languages without texts) with overrides. */
|
|
68
69
|
declare function resolveFilterBuilderMessages(locale: string, overrides?: Partial<FilterBuilderMessages>): FilterBuilderMessages;
|
|
69
70
|
|
|
70
71
|
declare const TEXT_DEBOUNCE_MS: number;
|
package/dist/index.js
CHANGED
|
@@ -154,6 +154,7 @@ function h(tag, props = {}, ...children) {
|
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
// src/i18n.ts
|
|
157
|
+
import { messageLanguage } from "@rastr/core";
|
|
157
158
|
var cs = {
|
|
158
159
|
label: "Filtr",
|
|
159
160
|
and: "A",
|
|
@@ -232,10 +233,126 @@ var en = {
|
|
|
232
233
|
isNotNull: "Is not blank"
|
|
233
234
|
}
|
|
234
235
|
};
|
|
235
|
-
var
|
|
236
|
+
var de = {
|
|
237
|
+
label: "Filter",
|
|
238
|
+
and: "Und",
|
|
239
|
+
or: "Oder",
|
|
240
|
+
nand: "Nicht und",
|
|
241
|
+
nor: "Nicht oder",
|
|
242
|
+
negate: "Negieren",
|
|
243
|
+
addCondition: "Bedingung hinzuf\xFCgen",
|
|
244
|
+
addGroup: "Gruppe hinzuf\xFCgen",
|
|
245
|
+
remove: "Entfernen",
|
|
246
|
+
field: "Feld",
|
|
247
|
+
operator: "Operator",
|
|
248
|
+
value: "Wert",
|
|
249
|
+
from: "Von",
|
|
250
|
+
to: "Bis",
|
|
251
|
+
choose: "(ausw\xE4hlen)",
|
|
252
|
+
yes: "Ja",
|
|
253
|
+
no: "Nein",
|
|
254
|
+
blanks: "(Leer)",
|
|
255
|
+
search: "Suchen\u2026",
|
|
256
|
+
empty: "Keine Bedingungen",
|
|
257
|
+
ops: {
|
|
258
|
+
eq: "Gleich",
|
|
259
|
+
neq: "Ungleich",
|
|
260
|
+
lt: "Kleiner als",
|
|
261
|
+
lte: "Kleiner oder gleich",
|
|
262
|
+
gt: "Gr\xF6\xDFer als",
|
|
263
|
+
gte: "Gr\xF6\xDFer oder gleich",
|
|
264
|
+
between: "Zwischen",
|
|
265
|
+
in: "Ist einer von",
|
|
266
|
+
notIn: "Ist keiner von",
|
|
267
|
+
contains: "Enth\xE4lt",
|
|
268
|
+
notContains: "Enth\xE4lt nicht",
|
|
269
|
+
startsWith: "Beginnt mit",
|
|
270
|
+
endsWith: "Endet mit",
|
|
271
|
+
isNull: "Ist leer",
|
|
272
|
+
isNotNull: "Ist nicht leer"
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
var pl = {
|
|
276
|
+
label: "Filtr",
|
|
277
|
+
and: "I",
|
|
278
|
+
or: "Lub",
|
|
279
|
+
nand: "Nie i",
|
|
280
|
+
nor: "Nie lub",
|
|
281
|
+
negate: "Zaneguj",
|
|
282
|
+
addCondition: "Dodaj warunek",
|
|
283
|
+
addGroup: "Dodaj grup\u0119",
|
|
284
|
+
remove: "Usu\u0144",
|
|
285
|
+
field: "Pole",
|
|
286
|
+
operator: "Operator",
|
|
287
|
+
value: "Warto\u015B\u0107",
|
|
288
|
+
from: "Od",
|
|
289
|
+
to: "Do",
|
|
290
|
+
choose: "(wybierz)",
|
|
291
|
+
yes: "Tak",
|
|
292
|
+
no: "Nie",
|
|
293
|
+
blanks: "(Puste)",
|
|
294
|
+
search: "Szukaj\u2026",
|
|
295
|
+
empty: "Brak warunk\xF3w",
|
|
296
|
+
ops: {
|
|
297
|
+
eq: "R\xF3wna si\u0119",
|
|
298
|
+
neq: "Nie r\xF3wna si\u0119",
|
|
299
|
+
lt: "Mniejsze ni\u017C",
|
|
300
|
+
lte: "Mniejsze lub r\xF3wne",
|
|
301
|
+
gt: "Wi\u0119ksze ni\u017C",
|
|
302
|
+
gte: "Wi\u0119ksze lub r\xF3wne",
|
|
303
|
+
between: "Pomi\u0119dzy",
|
|
304
|
+
in: "Jest jednym z",
|
|
305
|
+
notIn: "Nie jest \u017Cadnym z",
|
|
306
|
+
contains: "Zawiera",
|
|
307
|
+
notContains: "Nie zawiera",
|
|
308
|
+
startsWith: "Zaczyna si\u0119 od",
|
|
309
|
+
endsWith: "Ko\u0144czy si\u0119 na",
|
|
310
|
+
isNull: "Jest puste",
|
|
311
|
+
isNotNull: "Nie jest puste"
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
var sk = {
|
|
315
|
+
label: "Filter",
|
|
316
|
+
and: "A",
|
|
317
|
+
or: "Alebo",
|
|
318
|
+
nand: "Nie a",
|
|
319
|
+
nor: "Nie alebo",
|
|
320
|
+
negate: "Negova\u0165",
|
|
321
|
+
addCondition: "Prida\u0165 podmienku",
|
|
322
|
+
addGroup: "Prida\u0165 skupinu",
|
|
323
|
+
remove: "Odstr\xE1ni\u0165",
|
|
324
|
+
field: "Pole",
|
|
325
|
+
operator: "Oper\xE1tor",
|
|
326
|
+
value: "Hodnota",
|
|
327
|
+
from: "Od",
|
|
328
|
+
to: "Do",
|
|
329
|
+
choose: "(vyberte)",
|
|
330
|
+
yes: "\xC1no",
|
|
331
|
+
no: "Nie",
|
|
332
|
+
blanks: "(Pr\xE1zdne)",
|
|
333
|
+
search: "H\u013Eada\u0165\u2026",
|
|
334
|
+
empty: "\u017Diadne podmienky",
|
|
335
|
+
ops: {
|
|
336
|
+
eq: "Rovn\xE1 sa",
|
|
337
|
+
neq: "Nerovn\xE1 sa",
|
|
338
|
+
lt: "Men\u0161ie ako",
|
|
339
|
+
lte: "Men\u0161ie alebo rovn\xE9",
|
|
340
|
+
gt: "V\xE4\u010D\u0161ie ako",
|
|
341
|
+
gte: "V\xE4\u010D\u0161ie alebo rovn\xE9",
|
|
342
|
+
between: "Medzi",
|
|
343
|
+
in: "Je jedna z",
|
|
344
|
+
notIn: "Nie je \u017Eiadna z",
|
|
345
|
+
contains: "Obsahuje",
|
|
346
|
+
notContains: "Neobsahuje",
|
|
347
|
+
startsWith: "Za\u010D\xEDna na",
|
|
348
|
+
endsWith: "Kon\u010D\xED na",
|
|
349
|
+
isNull: "Je pr\xE1zdne",
|
|
350
|
+
isNotNull: "Nie je pr\xE1zdne"
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
var FILTER_BUILDER_MESSAGES = { cs, en, de, pl, sk };
|
|
236
354
|
function resolveFilterBuilderMessages(locale, overrides) {
|
|
237
|
-
|
|
238
|
-
return { ...language === "en" ? en : cs, ...overrides };
|
|
355
|
+
return { ...FILTER_BUILDER_MESSAGES[messageLanguage(locale)], ...overrides };
|
|
239
356
|
}
|
|
240
357
|
|
|
241
358
|
// src/builder.ts
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tree.ts","../src/builder.ts","../src/dom.ts","../src/i18n.ts"],"sourcesContent":["import { and, canonicalizeFilter, cond, isCondition, isGroup, isNot, normalizeFilter, not, or } from '@rastr/core';\nimport type { ColumnDef, ConditionNode, ConditionOp, DataType, FilterNode } from '@rastr/core';\n\n/** Indexes from the root group to a node. */\nexport type FilterPath = readonly number[];\n\nexport interface DraftCondition {\n readonly field: string | null;\n readonly op: ConditionOp | null;\n readonly value?: unknown;\n}\n\nexport interface DraftGroup {\n readonly kind: 'group';\n readonly op: 'and' | 'or';\n readonly negated: boolean;\n readonly items: readonly DraftNode[];\n}\n\nexport type DraftNode = DraftGroup | ({ readonly kind: 'condition' } & DraftCondition);\n\nexport type GroupPatch = { readonly op?: 'and' | 'or'; readonly negated?: boolean };\n\n/** Groups can be nested up to this many levels including the root. */\nexport const MAX_DEPTH: number = 4;\n\nconst TEXT_OPS: readonly ConditionOp[] = ['contains', 'notContains', 'startsWith', 'endsWith', 'eq', 'neq'];\nconst RANGE_OPS: readonly ConditionOp[] = ['eq', 'neq', 'lt', 'lte', 'gt', 'gte', 'between'];\nconst LIST_OPS: readonly ConditionOp[] = ['in', 'notIn'];\nconst NULL_OPS: readonly ConditionOp[] = ['isNull', 'isNotNull'];\n\n/** Operators offered for a column: the filter row's list plus in/notIn and null tests. */\nexport function operatorsFor(column: ColumnDef<unknown>): readonly ConditionOp[] {\n if (column.lookup) return ['eq', 'neq', ...LIST_OPS, ...NULL_OPS];\n const dataType: DataType = column.dataType ?? 'string';\n if (dataType === 'boolean') return ['eq', ...NULL_OPS];\n if (dataType === 'number' || dataType === 'date' || dataType === 'datetime') return [...RANGE_OPS, ...LIST_OPS, ...NULL_OPS];\n return [...TEXT_OPS, ...LIST_OPS, ...NULL_OPS];\n}\n\n/** How many values an operator takes; a changed arity clears the value. */\nexport function arityOf(op: ConditionOp | null): 'none' | 'single' | 'pair' | 'list' {\n if (op === null || op === 'isNull' || op === 'isNotNull') return 'none';\n if (op === 'between') return 'pair';\n if (op === 'in' || op === 'notIn') return 'list';\n return 'single';\n}\n\nconst emptyGroup = (op: 'and' | 'or' = 'and', negated: boolean = false, items: readonly DraftNode[] = []): DraftGroup => ({\n kind: 'group',\n op,\n negated,\n items,\n});\n\nconst draftCondition = (node: ConditionNode): DraftNode =>\n node.value === undefined ? { kind: 'condition', field: node.field, op: node.op } : { kind: 'condition', field: node.field, op: node.op, value: node.value };\n\n/** or(in, isNull) on one field, as written by the header filter with \"(Blanks)\" selected. */\nfunction foldBlanks(items: readonly FilterNode[]): ConditionNode | null {\n if (items.length !== 2 || !items.every(isCondition)) return null;\n const [first, second] = items as readonly ConditionNode[];\n const list: ConditionNode | undefined = [first, second].find(c => c?.op === 'in');\n const blanks: ConditionNode | undefined = [first, second].find(c => c?.op === 'isNull');\n if (!list || !blanks || list.field !== blanks.field || !Array.isArray(list.value)) return null;\n return cond(list.field, 'in', [...(list.value as unknown[]), null]);\n}\n\nfunction toDraftNode(node: FilterNode): DraftNode {\n if (isNot(node)) {\n const inner: DraftNode = toDraftNode(node.item);\n return inner.kind === 'group' ? { ...inner, negated: !inner.negated } : emptyGroup('and', true, [inner]);\n }\n if (isGroup(node)) {\n const folded: ConditionNode | null = node.op === 'or' ? foldBlanks(node.items) : null;\n if (folded) return draftCondition(folded);\n return emptyGroup(node.op, false, node.items.map(toDraftNode));\n }\n return draftCondition(node);\n}\n\n/** Editable tree for a filter; the root is always a group. */\nexport function toDraft(filter: FilterNode | null): DraftNode {\n const normalized: FilterNode | null = normalizeFilter(filter);\n if (normalized === null) return emptyGroup();\n const draft: DraftNode = toDraftNode(normalized);\n return draft.kind === 'group' ? draft : emptyGroup('and', false, [draft]);\n}\n\nconst isBlankValue = (value: unknown): boolean => value === undefined || value === null || value === '';\n\nfunction conditionFrom(node: DraftCondition): FilterNode | null {\n if (node.field === null || node.op === null) return null;\n const { field, op, value } = node;\n switch (arityOf(op)) {\n case 'none':\n return cond(field, op);\n case 'single':\n return isBlankValue(value) ? null : cond(field, op, value);\n case 'pair': {\n if (!Array.isArray(value) || value.length !== 2 || value.some(isBlankValue)) return null;\n return cond(field, op, value);\n }\n case 'list': {\n if (!Array.isArray(value)) return null;\n const values: unknown[] = value.filter(v => v !== null && v !== undefined);\n const blanks: boolean = values.length !== value.length;\n const list: FilterNode | null = values.length > 0 ? cond(field, op, values) : null;\n if (!blanks) return list;\n // A blanks entry is the null test the header filter writes next to the list.\n return op === 'in' ? or(list, cond(field, 'isNull')) : and(list, cond(field, 'isNotNull'));\n }\n }\n}\n\nfunction fromDraftNode(node: DraftNode): FilterNode | null {\n if (node.kind === 'condition') return conditionFrom(node);\n const items: (FilterNode | null)[] = node.items.map(fromDraftNode);\n const grouped: FilterNode | null = node.op === 'and' ? and(...items) : or(...items);\n return grouped && node.negated ? not(grouped) : grouped;\n}\n\n/** Filter for the draft: incomplete conditions are dropped and the result is canonical. */\nexport function fromDraft(draft: DraftNode, columns: readonly ColumnDef<unknown>[]): FilterNode | null {\n return canonicalizeFilter(fromDraftNode(draft), columns);\n}\n\n/** Replaces the node at path; paths that do not point into groups leave the tree unchanged. */\nfunction replaceAt(draft: DraftNode, path: FilterPath, replace: (node: DraftNode) => DraftNode | null): DraftNode {\n if (path.length === 0) return replace(draft) ?? emptyGroup();\n if (draft.kind !== 'group') return draft;\n const [index, ...rest] = path as [number, ...number[]];\n const child: DraftNode | undefined = draft.items[index];\n if (!child) return draft;\n if (rest.length === 0) {\n const next: DraftNode | null = replace(child);\n if (next === child) return draft;\n const items: DraftNode[] = [...draft.items];\n if (next === null) items.splice(index, 1);\n else items[index] = next;\n return { ...draft, items };\n }\n const next: DraftNode = replaceAt(child, rest, replace);\n if (next === child) return draft;\n const items: DraftNode[] = [...draft.items];\n items[index] = next;\n return { ...draft, items };\n}\n\nexport function addCondition(draft: DraftNode, path: FilterPath): DraftNode {\n return replaceAt(draft, path, node =>\n node.kind === 'group' ? { ...node, items: [...node.items, { kind: 'condition', field: null, op: null }] } : node,\n );\n}\n\n/** Adds an empty group; a group already at MAX_DEPTH returns the tree unchanged. */\nexport function addGroup(draft: DraftNode, path: FilterPath): DraftNode {\n if (path.length + 1 >= MAX_DEPTH) return draft;\n return replaceAt(draft, path, node => (node.kind === 'group' ? { ...node, items: [...node.items, emptyGroup()] } : node));\n}\n\nexport function removeNode(draft: DraftNode, path: FilterPath): DraftNode {\n return replaceAt(draft, path, () => null);\n}\n\n/**\n * Patches a condition or a group. A new field resets the operator to the column's first one\n * (null without columns) and clears the value; an operator of another arity clears the value.\n */\nexport function updateNode(\n draft: DraftNode,\n path: FilterPath,\n patch: Partial<DraftCondition> | GroupPatch,\n columns: readonly ColumnDef<unknown>[] = [],\n): DraftNode {\n return replaceAt(draft, path, node => {\n if (node.kind === 'group') {\n const groupPatch: GroupPatch = patch as GroupPatch;\n return { ...node, ...(groupPatch.op ? { op: groupPatch.op } : {}), ...(groupPatch.negated !== undefined ? { negated: groupPatch.negated } : {}) };\n }\n const conditionPatch: Partial<DraftCondition> = patch as Partial<DraftCondition>;\n if (conditionPatch.field !== undefined && conditionPatch.field !== node.field) {\n const column: ColumnDef<unknown> | undefined = columns.find(c => c.field === conditionPatch.field);\n return { kind: 'condition', field: conditionPatch.field, op: column ? (operatorsFor(column)[0] ?? null) : null };\n }\n const op: ConditionOp | null = conditionPatch.op !== undefined ? conditionPatch.op : node.op;\n const keepValue: boolean = arityOf(op) === arityOf(node.op);\n const value: unknown = 'value' in conditionPatch ? conditionPatch.value : keepValue ? node.value : undefined;\n return value === undefined ? { kind: 'condition', field: node.field, op } : { kind: 'condition', field: node.field, op, value };\n });\n}\n","import { canonicalizeFilter } from '@rastr/core';\nimport type { ColumnDef, ConditionOp, FilterNode, LookupItem } from '@rastr/core';\nimport { closeControlPopover, createDateInput, createSelect } from '@rastr/controls';\nimport type { DateInputControl, SelectControl } from '@rastr/controls';\nimport { h } from './dom';\nimport { resolveFilterBuilderMessages } from './i18n';\nimport type { FilterBuilderMessages } from './i18n';\nimport { addCondition, addGroup, arityOf, fromDraft, MAX_DEPTH, operatorsFor, removeNode, toDraft, updateNode } from './tree';\nimport type { DraftCondition, DraftGroup, DraftNode, FilterPath } from './tree';\n\nexport const TEXT_DEBOUNCE_MS: number = 300;\n\nexport interface FilterBuilderOptions {\n readonly columns: readonly ColumnDef<unknown>[];\n readonly value: FilterNode | null;\n readonly onChange: (value: FilterNode | null) => void;\n readonly locale?: string;\n readonly messages?: Partial<FilterBuilderMessages>;\n /** Distinct values for in/notIn on columns without a lookup, typically store.loadDistinct. */\n readonly loadValues?: (field: string, search: string) => Promise<readonly unknown[]>;\n}\n\nexport interface FilterBuilder {\n setValue(value: FilterNode | null): void;\n /** Reports a typed value still waiting for the debounce (call before using the value, e.g. on save). */\n flush(): void;\n /** Flushes a pending value, then removes the builder. */\n destroy(): void;\n}\n\ntype ConditionDraft = { readonly kind: 'condition' } & DraftCondition;\ntype ValueKind = 'text' | 'number' | 'date' | 'datetime' | 'boolean' | 'lookup';\n\nconst pathKey = (path: FilterPath): string => path.join('.');\n\nfunction valueKind(column: ColumnDef<unknown> | undefined): ValueKind {\n if (!column) return 'text';\n if (column.lookup) return 'lookup';\n const dataType: string = column.dataType ?? 'string';\n if (dataType === 'number' || dataType === 'date' || dataType === 'datetime' || dataType === 'boolean') return dataType;\n return 'text';\n}\n\n/** \"1 234,5\" → 1234.5; empty or unparsable text is no value. */\nfunction parseNumber(text: string): number | undefined {\n const trimmed: string = text.trim();\n if (trimmed === '') return undefined;\n const parsed: number = Number(trimmed.replace(/\\s/g, '').replace(',', '.'));\n return Number.isNaN(parsed) ? undefined : parsed;\n}\n\nfunction formatInput(value: unknown, kind: ValueKind): string {\n if (value === null || value === undefined) return '';\n if (kind === 'number' && typeof value === 'number') return String(value).replace('.', ',');\n return String(value);\n}\n\nfunction parseInput(text: string, kind: ValueKind): unknown {\n if (kind === 'number') return parseNumber(text);\n if (text === '') return undefined;\n return text;\n}\n\nconst sameFilter = (a: FilterNode | null, b: FilterNode | null): boolean => JSON.stringify(a) === JSON.stringify(b);\n\n/** Visual editor of a filter AST: groups with and/or and negation, conditions per column. */\nexport function createFilterBuilder(container: HTMLElement, options: FilterBuilderOptions): FilterBuilder {\n const messages: FilterBuilderMessages = resolveFilterBuilderMessages(options.locale ?? 'cs', options.messages);\n // Columns the grid does not filter by (row numbers, actions, filterable: false) are not offered.\n const columns: readonly ColumnDef<unknown>[] = options.columns.filter(c => c.filterable !== false);\n const root: HTMLDivElement = h('div', { class: 'rs-fb', attrs: { role: 'tree', 'aria-label': messages.label } });\n container.append(root);\n // Delete removes the focused condition, except while editing text in an input.\n root.addEventListener('keydown', event => {\n const target: HTMLElement = event.target as HTMLElement;\n if (event.key !== 'Delete' || (target instanceof HTMLInputElement && target.type !== 'checkbox')) return;\n const condition: HTMLElement | null = target.closest('.rs-fb__condition');\n const path: string | null = condition?.getAttribute('data-path') ?? null;\n if (path === null) return;\n event.preventDefault();\n const indexes: number[] = path.split('.').map(Number);\n const parent: string = indexes.slice(0, -1).join('.');\n pendingFocus = `${parent}:add-condition`;\n change(removeNode(draft, indexes));\n });\n let draft: DraftNode = toDraft(canonicalize(options.value));\n let timer: ReturnType<typeof setTimeout> | null = null;\n let destroyed: boolean = false;\n let pendingFocus: string | null = null;\n\n function canonicalize(value: FilterNode | null): FilterNode | null {\n try {\n return canonicalizeFilter(value, columns);\n } catch {\n return value;\n }\n }\n\n function current(): FilterNode | null {\n try {\n return fromDraft(draft, columns);\n } catch {\n return null;\n }\n }\n\n const emit = (): void => {\n if (timer !== null) clearTimeout(timer);\n timer = null;\n options.onChange(current());\n };\n const flush = (): void => {\n if (timer !== null) emit();\n };\n const emitLater = (): void => {\n if (timer !== null) clearTimeout(timer);\n timer = setTimeout(emit, TEXT_DEBOUNCE_MS);\n };\n\n /** Structural change: new tree, re-render, report at once. */\n const change = (next: DraftNode): void => {\n if (next === draft) return;\n draft = next;\n render();\n emit();\n };\n /** Value typed into an input: no re-render, so the input keeps focus and caret. */\n const setValueQuietly = (path: FilterPath, value: unknown, debounce: boolean): void => {\n draft = updateNode(draft, path, { value });\n if (debounce) emitLater();\n else emit();\n };\n\n const select = (\n cls: string,\n label: string,\n focusId: string,\n items: readonly { value: string; text: string }[],\n selected: string,\n onChange: (value: string) => void,\n ): HTMLButtonElement => {\n const control: SelectControl = createSelect({\n options: items.map(item => ({ value: item.value, label: item.text })),\n value: selected,\n label,\n className: `rs-fb__select ${cls}`,\n onChange,\n });\n control.element.setAttribute('data-fb-focus', focusId);\n return control.element;\n };\n const button = (text: string, cls: string, focusId: string, onClick: () => void, label?: string): HTMLButtonElement => {\n const el: HTMLButtonElement = h('button', { class: `rs-fb__btn ${cls}`, attrs: { type: 'button', 'data-fb-focus': focusId, 'aria-label': label } }, text);\n el.addEventListener('click', onClick);\n return el;\n };\n\n function renderInput(kind: ValueKind, value: unknown, focusId: string, label: string, onValue: (v: unknown) => void): HTMLElement {\n if (kind === 'date' || kind === 'datetime') {\n const date: DateInputControl = createDateInput({\n kind,\n value: typeof value === 'string' && value !== '' ? value : null,\n locale: options.locale ?? 'cs',\n label,\n className: 'rs-fb__date',\n onChange: next => {\n onValue(next ?? undefined);\n flush();\n },\n });\n date.input.setAttribute('data-fb-focus', focusId);\n return date.element;\n }\n const input: HTMLInputElement = h('input', {\n class: 'rs-fb__input',\n attrs: { type: 'text', 'aria-label': label, 'data-fb-focus': focusId, inputmode: kind === 'number' ? 'decimal' : null },\n });\n input.value = formatInput(value, kind);\n input.addEventListener('input', () => onValue(parseInput(input.value, kind)));\n // Leaving the input reports the value at once instead of after the debounce.\n input.addEventListener('change', flush);\n return input;\n }\n\n function lookupItems(column: ColumnDef<unknown>): readonly LookupItem[] {\n return column.lookup ?? [];\n }\n\n /** Checkbox list for in/notIn; values come from the lookup or from loadValues with a search box. */\n function renderList(path: FilterPath, column: ColumnDef<unknown>, value: unknown, focusId: string): HTMLElement {\n const selected: unknown[] = Array.isArray(value) ? [...value] : [];\n const wrapper: HTMLDivElement = h('div', { class: 'rs-fb__list-wrapper' });\n const list: HTMLDivElement = h('div', { class: 'rs-fb__list', attrs: { role: 'group', 'aria-label': messages.value } });\n const isSelected = (v: unknown): boolean => selected.some(s => Object.is(s, v));\n const fill = (items: readonly { value: unknown; text: string }[]): void => {\n // Selected values stay visible even when the current search does not return them.\n const missing: { value: unknown; text: string }[] = selected\n .filter(v => v !== null && !items.some(i => Object.is(i.value, v)))\n .map(v => ({ value: v, text: String(v) }));\n list.replaceChildren(\n ...[...missing, ...items, { value: null, text: messages.blanks }].map((item, index) => {\n const box: HTMLInputElement = h('input', { class: 'rs-fb__check', attrs: { type: 'checkbox', 'data-fb-focus': `${focusId}:${index}` } });\n box.checked = isSelected(item.value);\n box.addEventListener('change', () => {\n const at: number = selected.findIndex(s => Object.is(s, item.value));\n if (box.checked && at < 0) selected.push(item.value);\n if (!box.checked && at >= 0) selected.splice(at, 1);\n setValueQuietly(path, [...selected], false);\n });\n return h('label', { class: 'rs-fb__list-item' }, box, item.text);\n }),\n );\n };\n if (column.lookup) {\n fill(lookupItems(column).map(item => ({ value: item.value, text: item.text })));\n } else if (options.loadValues) {\n const load = options.loadValues;\n const search: HTMLInputElement = h('input', { class: 'rs-fb__input rs-fb__search', attrs: { type: 'search', placeholder: messages.search, 'aria-label': messages.search, 'data-fb-focus': `${focusId}:search` } });\n let searchTimer: ReturnType<typeof setTimeout> | null = null;\n let request: number = 0;\n const run = (text: string): void => {\n const id: number = ++request;\n void load(column.field, text).then(values => {\n if (id !== request || destroyed) return;\n fill(values.filter(v => v !== null && v !== undefined).map(v => ({ value: v, text: String(v) })));\n });\n };\n search.addEventListener('input', () => {\n if (searchTimer !== null) clearTimeout(searchTimer);\n searchTimer = setTimeout(() => run(search.value), TEXT_DEBOUNCE_MS);\n });\n wrapper.append(search);\n fill([]);\n run('');\n } else {\n fill([]);\n }\n wrapper.append(list);\n return wrapper;\n }\n\n function renderValue(path: FilterPath, node: ConditionDraft, column: ColumnDef<unknown> | undefined): HTMLElement {\n const cell: HTMLSpanElement = h('span', { class: 'rs-fb__value' });\n const id: string = pathKey(path);\n const kind: ValueKind = valueKind(column);\n const arity = arityOf(node.op);\n if (!column || arity === 'none') return cell;\n if (arity === 'list') {\n cell.append(renderList(path, column, node.value, `${id}:list`));\n return cell;\n }\n if (kind === 'lookup' || kind === 'boolean') {\n const items: readonly LookupItem[] =\n kind === 'boolean' ? [{ value: true, text: messages.yes }, { value: false, text: messages.no }] : lookupItems(column);\n const index: number = items.findIndex(item => Object.is(item.value, node.value));\n const el: HTMLButtonElement = select(\n 'rs-fb__value-select',\n messages.value,\n `${id}:value`,\n [{ value: '', text: messages.choose }, ...items.map((item, i) => ({ value: String(i), text: item.text }))],\n index < 0 ? '' : String(index),\n value => setValueQuietly(path, value === '' ? undefined : items[Number(value)]?.value, false),\n );\n cell.append(el);\n return cell;\n }\n if (arity === 'pair') {\n const pair: unknown[] = Array.isArray(node.value) ? [...node.value] : [undefined, undefined];\n const setAt = (index: number) => (v: unknown): void => {\n pair[index] = v;\n setValueQuietly(path, [...pair], true);\n };\n cell.append(\n renderInput(kind, pair[0], `${id}:from`, messages.from, setAt(0)),\n h('span', { class: 'rs-fb__dash', attrs: { 'aria-hidden': 'true' } }, '–'),\n renderInput(kind, pair[1], `${id}:to`, messages.to, setAt(1)),\n );\n return cell;\n }\n cell.append(renderInput(kind, node.value, `${id}:value`, messages.value, v => setValueQuietly(path, v, true)));\n return cell;\n }\n\n function renderCondition(path: FilterPath, node: ConditionDraft, level: number): HTMLElement {\n const id: string = pathKey(path);\n const column: ColumnDef<unknown> | undefined = columns.find(c => c.field === node.field);\n const field: HTMLButtonElement = select(\n 'rs-fb__field',\n messages.field,\n `${id}:field`,\n [{ value: '', text: messages.choose }, ...columns.map(c => ({ value: c.field, text: c.caption ?? c.field }))],\n node.field ?? '',\n value => change(updateNode(draft, path, { field: value === '' ? null : value }, columns)),\n );\n const ops: readonly ConditionOp[] = column ? operatorsFor(column) : [];\n const operator: HTMLButtonElement = select(\n 'rs-fb__operator',\n messages.operator,\n `${id}:op`,\n ops.map(op => ({ value: op, text: messages.ops[op] })),\n node.op ?? '',\n value => change(updateNode(draft, path, { op: value as ConditionOp })),\n );\n operator.disabled = !column;\n return h(\n 'div',\n { class: 'rs-fb__condition', attrs: { role: 'treeitem', 'aria-level': level, 'data-path': id } },\n field,\n operator,\n renderValue(path, node, column),\n button('×', 'rs-fb__remove', `${id}:remove`, () => change(removeNode(draft, path)), messages.remove),\n );\n }\n\n function renderGroup(path: FilterPath, node: DraftGroup, level: number): HTMLElement {\n const id: string = pathKey(path);\n const labels: Record<'and' | 'or', string> = node.negated ? { and: messages.nand, or: messages.nor } : { and: messages.and, or: messages.or };\n const ops: HTMLDivElement = h('div', { class: 'rs-fb__ops', attrs: { role: 'group' } });\n for (const op of ['and', 'or'] as const) {\n const el: HTMLButtonElement = button(labels[op], 'rs-fb__op', `${id}:${op}`, () => change(updateNode(draft, path, { op })));\n el.setAttribute('aria-pressed', String(node.op === op));\n ops.append(el);\n }\n const negate: HTMLInputElement = h('input', { class: 'rs-fb__check', attrs: { type: 'checkbox', 'data-fb-focus': `${id}:negate` } });\n negate.checked = node.negated;\n negate.addEventListener('change', () => change(updateNode(draft, path, { negated: negate.checked })));\n const head: HTMLDivElement = h(\n 'div',\n { class: 'rs-fb__group-head' },\n ops,\n h('label', { class: 'rs-fb__negate' }, negate, messages.negate),\n button(messages.addCondition, 'rs-fb__add', `${id}:add-condition`, () => change(addCondition(draft, path))),\n level < MAX_DEPTH ? button(messages.addGroup, 'rs-fb__add', `${id}:add-group`, () => change(addGroup(draft, path))) : null,\n path.length > 0 ? button('×', 'rs-fb__remove', `${id}:remove`, () => change(removeNode(draft, path)), messages.remove) : null,\n );\n const items: HTMLDivElement = h('div', { class: 'rs-fb__items', attrs: { role: 'group' } });\n node.items.forEach((child, index) => {\n const childPath: FilterPath = [...path, index];\n items.append(child.kind === 'group' ? renderGroup(childPath, child, level + 1) : renderCondition(childPath, child, level + 1));\n });\n if (node.items.length === 0) items.append(h('div', { class: 'rs-fb__empty' }, messages.empty));\n return h(\n 'div',\n { class: 'rs-fb__group', attrs: { role: 'treeitem', 'aria-level': level, 'aria-expanded': 'true', 'data-path': id, 'data-negated': node.negated } },\n head,\n items,\n );\n }\n\n function render(): void {\n // Re-rendering replaces the controls; focus returns to the control with the same id.\n const active: string | null = (document.activeElement as HTMLElement | null)?.closest?.('.rs-fb') === root\n ? (document.activeElement?.getAttribute('data-fb-focus') ?? null)\n : null;\n const focused: string | null = pendingFocus ?? active;\n pendingFocus = null;\n closeControlPopover(root);\n root.replaceChildren(draft.kind === 'group' ? renderGroup([], draft, 1) : h('div'));\n if (focused) [...root.querySelectorAll<HTMLElement>('[data-fb-focus]')].find(el => el.getAttribute('data-fb-focus') === focused)?.focus();\n }\n\n render();\n\n return {\n setValue(value: FilterNode | null): void {\n const next: FilterNode | null = canonicalize(value);\n // The same filter keeps the draft, including conditions that are still being filled in.\n if (sameFilter(next, current())) return;\n draft = toDraft(next);\n render();\n },\n flush,\n destroy(): void {\n flush();\n destroyed = true;\n closeControlPopover(root);\n root.remove();\n },\n };\n}\n","type AttrValue = string | number | boolean | null | undefined;\ntype Child = Node | string | null | undefined | false;\n\n/** Creates an element; strings are text nodes, markup is never parsed. */\nexport function h<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n props: { readonly class?: string; readonly attrs?: Readonly<Record<string, AttrValue>> } = {},\n ...children: Child[]\n): HTMLElementTagNameMap[K] {\n const el: HTMLElementTagNameMap[K] = document.createElement(tag);\n if (props.class) el.className = props.class;\n for (const [name, value] of Object.entries(props.attrs ?? {})) {\n if (value === false || value === null || value === undefined) continue;\n el.setAttribute(name, value === true ? '' : String(value));\n }\n for (const child of children) {\n if (child !== null && child !== undefined && child !== false) el.append(child);\n }\n return el;\n}\n","import type { ConditionOp } from '@rastr/core';\n\nexport interface FilterBuilderMessages {\n /** Accessible name of the tree. */\n readonly label: string;\n readonly and: string;\n readonly or: string;\n /** Negated \"and\" group (NAND). */\n readonly nand: string;\n /** Negated \"or\" group (NOR). */\n readonly nor: string;\n readonly negate: string;\n readonly addCondition: string;\n readonly addGroup: string;\n readonly remove: string;\n readonly field: string;\n readonly operator: string;\n readonly value: string;\n readonly from: string;\n readonly to: string;\n readonly choose: string;\n readonly yes: string;\n readonly no: string;\n readonly blanks: string;\n readonly search: string;\n readonly empty: string;\n readonly ops: Readonly<Record<ConditionOp, string>>;\n}\n\nconst cs: FilterBuilderMessages = {\n label: 'Filtr',\n and: 'A',\n or: 'Nebo',\n nand: 'Ne a',\n nor: 'Ne nebo',\n negate: 'Negovat',\n addCondition: 'Přidat podmínku',\n addGroup: 'Přidat skupinu',\n remove: 'Odebrat',\n field: 'Pole',\n operator: 'Operátor',\n value: 'Hodnota',\n from: 'Od',\n to: 'Do',\n choose: '(vyberte)',\n yes: 'Ano',\n no: 'Ne',\n blanks: '(Prázdné)',\n search: 'Hledat…',\n empty: 'Žádné podmínky',\n ops: {\n eq: 'Rovná se',\n neq: 'Nerovná se',\n lt: 'Menší než',\n lte: 'Menší nebo rovno',\n gt: 'Větší než',\n gte: 'Větší nebo rovno',\n between: 'Mezi',\n in: 'Je jedna z',\n notIn: 'Není žádná z',\n contains: 'Obsahuje',\n notContains: 'Neobsahuje',\n startsWith: 'Začíná na',\n endsWith: 'Končí na',\n isNull: 'Je prázdné',\n isNotNull: 'Není prázdné',\n },\n};\n\nconst en: FilterBuilderMessages = {\n label: 'Filter',\n and: 'And',\n or: 'Or',\n nand: 'Not and',\n nor: 'Not or',\n negate: 'Negate',\n addCondition: 'Add condition',\n addGroup: 'Add group',\n remove: 'Remove',\n field: 'Field',\n operator: 'Operator',\n value: 'Value',\n from: 'From',\n to: 'To',\n choose: '(choose)',\n yes: 'Yes',\n no: 'No',\n blanks: '(Blanks)',\n search: 'Search…',\n empty: 'No conditions',\n ops: {\n eq: 'Equals',\n neq: 'Does not equal',\n lt: 'Less than',\n lte: 'Less than or equal',\n gt: 'Greater than',\n gte: 'Greater than or equal',\n between: 'Between',\n in: 'Is any of',\n notIn: 'Is none of',\n contains: 'Contains',\n notContains: 'Does not contain',\n startsWith: 'Starts with',\n endsWith: 'Ends with',\n isNull: 'Is blank',\n isNotNull: 'Is not blank',\n },\n};\n\nexport const FILTER_BUILDER_MESSAGES: Readonly<Record<'cs' | 'en', FilterBuilderMessages>> = { cs, en };\n\nexport function resolveFilterBuilderMessages(locale: string, overrides?: Partial<FilterBuilderMessages>): FilterBuilderMessages {\n const language: string = locale.toLowerCase().split('-')[0] ?? 'cs';\n return { ...(language === 'en' ? en : cs), ...overrides };\n}\n"],"mappings":"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","names":["next","items","canonicalizeFilter","canonicalizeFilter"]}
|
|
1
|
+
{"version":3,"sources":["../src/tree.ts","../src/builder.ts","../src/dom.ts","../src/i18n.ts"],"sourcesContent":["import { and, canonicalizeFilter, cond, isCondition, isGroup, isNot, normalizeFilter, not, or } from '@rastr/core';\nimport type { ColumnDef, ConditionNode, ConditionOp, DataType, FilterNode } from '@rastr/core';\n\n/** Indexes from the root group to a node. */\nexport type FilterPath = readonly number[];\n\nexport interface DraftCondition {\n readonly field: string | null;\n readonly op: ConditionOp | null;\n readonly value?: unknown;\n}\n\nexport interface DraftGroup {\n readonly kind: 'group';\n readonly op: 'and' | 'or';\n readonly negated: boolean;\n readonly items: readonly DraftNode[];\n}\n\nexport type DraftNode = DraftGroup | ({ readonly kind: 'condition' } & DraftCondition);\n\nexport type GroupPatch = { readonly op?: 'and' | 'or'; readonly negated?: boolean };\n\n/** Groups can be nested up to this many levels including the root. */\nexport const MAX_DEPTH: number = 4;\n\nconst TEXT_OPS: readonly ConditionOp[] = ['contains', 'notContains', 'startsWith', 'endsWith', 'eq', 'neq'];\nconst RANGE_OPS: readonly ConditionOp[] = ['eq', 'neq', 'lt', 'lte', 'gt', 'gte', 'between'];\nconst LIST_OPS: readonly ConditionOp[] = ['in', 'notIn'];\nconst NULL_OPS: readonly ConditionOp[] = ['isNull', 'isNotNull'];\n\n/** Operators offered for a column: the filter row's list plus in/notIn and null tests. */\nexport function operatorsFor(column: ColumnDef<unknown>): readonly ConditionOp[] {\n if (column.lookup) return ['eq', 'neq', ...LIST_OPS, ...NULL_OPS];\n const dataType: DataType = column.dataType ?? 'string';\n if (dataType === 'boolean') return ['eq', ...NULL_OPS];\n if (dataType === 'number' || dataType === 'date' || dataType === 'datetime') return [...RANGE_OPS, ...LIST_OPS, ...NULL_OPS];\n return [...TEXT_OPS, ...LIST_OPS, ...NULL_OPS];\n}\n\n/** How many values an operator takes; a changed arity clears the value. */\nexport function arityOf(op: ConditionOp | null): 'none' | 'single' | 'pair' | 'list' {\n if (op === null || op === 'isNull' || op === 'isNotNull') return 'none';\n if (op === 'between') return 'pair';\n if (op === 'in' || op === 'notIn') return 'list';\n return 'single';\n}\n\nconst emptyGroup = (op: 'and' | 'or' = 'and', negated: boolean = false, items: readonly DraftNode[] = []): DraftGroup => ({\n kind: 'group',\n op,\n negated,\n items,\n});\n\nconst draftCondition = (node: ConditionNode): DraftNode =>\n node.value === undefined ? { kind: 'condition', field: node.field, op: node.op } : { kind: 'condition', field: node.field, op: node.op, value: node.value };\n\n/** or(in, isNull) on one field, as written by the header filter with \"(Blanks)\" selected. */\nfunction foldBlanks(items: readonly FilterNode[]): ConditionNode | null {\n if (items.length !== 2 || !items.every(isCondition)) return null;\n const [first, second] = items as readonly ConditionNode[];\n const list: ConditionNode | undefined = [first, second].find(c => c?.op === 'in');\n const blanks: ConditionNode | undefined = [first, second].find(c => c?.op === 'isNull');\n if (!list || !blanks || list.field !== blanks.field || !Array.isArray(list.value)) return null;\n return cond(list.field, 'in', [...(list.value as unknown[]), null]);\n}\n\nfunction toDraftNode(node: FilterNode): DraftNode {\n if (isNot(node)) {\n const inner: DraftNode = toDraftNode(node.item);\n return inner.kind === 'group' ? { ...inner, negated: !inner.negated } : emptyGroup('and', true, [inner]);\n }\n if (isGroup(node)) {\n const folded: ConditionNode | null = node.op === 'or' ? foldBlanks(node.items) : null;\n if (folded) return draftCondition(folded);\n return emptyGroup(node.op, false, node.items.map(toDraftNode));\n }\n return draftCondition(node);\n}\n\n/** Editable tree for a filter; the root is always a group. */\nexport function toDraft(filter: FilterNode | null): DraftNode {\n const normalized: FilterNode | null = normalizeFilter(filter);\n if (normalized === null) return emptyGroup();\n const draft: DraftNode = toDraftNode(normalized);\n return draft.kind === 'group' ? draft : emptyGroup('and', false, [draft]);\n}\n\nconst isBlankValue = (value: unknown): boolean => value === undefined || value === null || value === '';\n\nfunction conditionFrom(node: DraftCondition): FilterNode | null {\n if (node.field === null || node.op === null) return null;\n const { field, op, value } = node;\n switch (arityOf(op)) {\n case 'none':\n return cond(field, op);\n case 'single':\n return isBlankValue(value) ? null : cond(field, op, value);\n case 'pair': {\n if (!Array.isArray(value) || value.length !== 2 || value.some(isBlankValue)) return null;\n return cond(field, op, value);\n }\n case 'list': {\n if (!Array.isArray(value)) return null;\n const values: unknown[] = value.filter(v => v !== null && v !== undefined);\n const blanks: boolean = values.length !== value.length;\n const list: FilterNode | null = values.length > 0 ? cond(field, op, values) : null;\n if (!blanks) return list;\n // A blanks entry is the null test the header filter writes next to the list.\n return op === 'in' ? or(list, cond(field, 'isNull')) : and(list, cond(field, 'isNotNull'));\n }\n }\n}\n\nfunction fromDraftNode(node: DraftNode): FilterNode | null {\n if (node.kind === 'condition') return conditionFrom(node);\n const items: (FilterNode | null)[] = node.items.map(fromDraftNode);\n const grouped: FilterNode | null = node.op === 'and' ? and(...items) : or(...items);\n return grouped && node.negated ? not(grouped) : grouped;\n}\n\n/** Filter for the draft: incomplete conditions are dropped and the result is canonical. */\nexport function fromDraft(draft: DraftNode, columns: readonly ColumnDef<unknown>[]): FilterNode | null {\n return canonicalizeFilter(fromDraftNode(draft), columns);\n}\n\n/** Replaces the node at path; paths that do not point into groups leave the tree unchanged. */\nfunction replaceAt(draft: DraftNode, path: FilterPath, replace: (node: DraftNode) => DraftNode | null): DraftNode {\n if (path.length === 0) return replace(draft) ?? emptyGroup();\n if (draft.kind !== 'group') return draft;\n const [index, ...rest] = path as [number, ...number[]];\n const child: DraftNode | undefined = draft.items[index];\n if (!child) return draft;\n if (rest.length === 0) {\n const next: DraftNode | null = replace(child);\n if (next === child) return draft;\n const items: DraftNode[] = [...draft.items];\n if (next === null) items.splice(index, 1);\n else items[index] = next;\n return { ...draft, items };\n }\n const next: DraftNode = replaceAt(child, rest, replace);\n if (next === child) return draft;\n const items: DraftNode[] = [...draft.items];\n items[index] = next;\n return { ...draft, items };\n}\n\nexport function addCondition(draft: DraftNode, path: FilterPath): DraftNode {\n return replaceAt(draft, path, node =>\n node.kind === 'group' ? { ...node, items: [...node.items, { kind: 'condition', field: null, op: null }] } : node,\n );\n}\n\n/** Adds an empty group; a group already at MAX_DEPTH returns the tree unchanged. */\nexport function addGroup(draft: DraftNode, path: FilterPath): DraftNode {\n if (path.length + 1 >= MAX_DEPTH) return draft;\n return replaceAt(draft, path, node => (node.kind === 'group' ? { ...node, items: [...node.items, emptyGroup()] } : node));\n}\n\nexport function removeNode(draft: DraftNode, path: FilterPath): DraftNode {\n return replaceAt(draft, path, () => null);\n}\n\n/**\n * Patches a condition or a group. A new field resets the operator to the column's first one\n * (null without columns) and clears the value; an operator of another arity clears the value.\n */\nexport function updateNode(\n draft: DraftNode,\n path: FilterPath,\n patch: Partial<DraftCondition> | GroupPatch,\n columns: readonly ColumnDef<unknown>[] = [],\n): DraftNode {\n return replaceAt(draft, path, node => {\n if (node.kind === 'group') {\n const groupPatch: GroupPatch = patch as GroupPatch;\n return { ...node, ...(groupPatch.op ? { op: groupPatch.op } : {}), ...(groupPatch.negated !== undefined ? { negated: groupPatch.negated } : {}) };\n }\n const conditionPatch: Partial<DraftCondition> = patch as Partial<DraftCondition>;\n if (conditionPatch.field !== undefined && conditionPatch.field !== node.field) {\n const column: ColumnDef<unknown> | undefined = columns.find(c => c.field === conditionPatch.field);\n return { kind: 'condition', field: conditionPatch.field, op: column ? (operatorsFor(column)[0] ?? null) : null };\n }\n const op: ConditionOp | null = conditionPatch.op !== undefined ? conditionPatch.op : node.op;\n const keepValue: boolean = arityOf(op) === arityOf(node.op);\n const value: unknown = 'value' in conditionPatch ? conditionPatch.value : keepValue ? node.value : undefined;\n return value === undefined ? { kind: 'condition', field: node.field, op } : { kind: 'condition', field: node.field, op, value };\n });\n}\n","import { canonicalizeFilter } from '@rastr/core';\nimport type { ColumnDef, ConditionOp, FilterNode, LookupItem } from '@rastr/core';\nimport { closeControlPopover, createDateInput, createSelect } from '@rastr/controls';\nimport type { DateInputControl, SelectControl } from '@rastr/controls';\nimport { h } from './dom';\nimport { resolveFilterBuilderMessages } from './i18n';\nimport type { FilterBuilderMessages } from './i18n';\nimport { addCondition, addGroup, arityOf, fromDraft, MAX_DEPTH, operatorsFor, removeNode, toDraft, updateNode } from './tree';\nimport type { DraftCondition, DraftGroup, DraftNode, FilterPath } from './tree';\n\nexport const TEXT_DEBOUNCE_MS: number = 300;\n\nexport interface FilterBuilderOptions {\n readonly columns: readonly ColumnDef<unknown>[];\n readonly value: FilterNode | null;\n readonly onChange: (value: FilterNode | null) => void;\n readonly locale?: string;\n readonly messages?: Partial<FilterBuilderMessages>;\n /** Distinct values for in/notIn on columns without a lookup, typically store.loadDistinct. */\n readonly loadValues?: (field: string, search: string) => Promise<readonly unknown[]>;\n}\n\nexport interface FilterBuilder {\n setValue(value: FilterNode | null): void;\n /** Reports a typed value still waiting for the debounce (call before using the value, e.g. on save). */\n flush(): void;\n /** Flushes a pending value, then removes the builder. */\n destroy(): void;\n}\n\ntype ConditionDraft = { readonly kind: 'condition' } & DraftCondition;\ntype ValueKind = 'text' | 'number' | 'date' | 'datetime' | 'boolean' | 'lookup';\n\nconst pathKey = (path: FilterPath): string => path.join('.');\n\nfunction valueKind(column: ColumnDef<unknown> | undefined): ValueKind {\n if (!column) return 'text';\n if (column.lookup) return 'lookup';\n const dataType: string = column.dataType ?? 'string';\n if (dataType === 'number' || dataType === 'date' || dataType === 'datetime' || dataType === 'boolean') return dataType;\n return 'text';\n}\n\n/** \"1 234,5\" → 1234.5; empty or unparsable text is no value. */\nfunction parseNumber(text: string): number | undefined {\n const trimmed: string = text.trim();\n if (trimmed === '') return undefined;\n const parsed: number = Number(trimmed.replace(/\\s/g, '').replace(',', '.'));\n return Number.isNaN(parsed) ? undefined : parsed;\n}\n\nfunction formatInput(value: unknown, kind: ValueKind): string {\n if (value === null || value === undefined) return '';\n if (kind === 'number' && typeof value === 'number') return String(value).replace('.', ',');\n return String(value);\n}\n\nfunction parseInput(text: string, kind: ValueKind): unknown {\n if (kind === 'number') return parseNumber(text);\n if (text === '') return undefined;\n return text;\n}\n\nconst sameFilter = (a: FilterNode | null, b: FilterNode | null): boolean => JSON.stringify(a) === JSON.stringify(b);\n\n/** Visual editor of a filter AST: groups with and/or and negation, conditions per column. */\nexport function createFilterBuilder(container: HTMLElement, options: FilterBuilderOptions): FilterBuilder {\n const messages: FilterBuilderMessages = resolveFilterBuilderMessages(options.locale ?? 'cs', options.messages);\n // Columns the grid does not filter by (row numbers, actions, filterable: false) are not offered.\n const columns: readonly ColumnDef<unknown>[] = options.columns.filter(c => c.filterable !== false);\n const root: HTMLDivElement = h('div', { class: 'rs-fb', attrs: { role: 'tree', 'aria-label': messages.label } });\n container.append(root);\n // Delete removes the focused condition, except while editing text in an input.\n root.addEventListener('keydown', event => {\n const target: HTMLElement = event.target as HTMLElement;\n if (event.key !== 'Delete' || (target instanceof HTMLInputElement && target.type !== 'checkbox')) return;\n const condition: HTMLElement | null = target.closest('.rs-fb__condition');\n const path: string | null = condition?.getAttribute('data-path') ?? null;\n if (path === null) return;\n event.preventDefault();\n const indexes: number[] = path.split('.').map(Number);\n const parent: string = indexes.slice(0, -1).join('.');\n pendingFocus = `${parent}:add-condition`;\n change(removeNode(draft, indexes));\n });\n let draft: DraftNode = toDraft(canonicalize(options.value));\n let timer: ReturnType<typeof setTimeout> | null = null;\n let destroyed: boolean = false;\n let pendingFocus: string | null = null;\n\n function canonicalize(value: FilterNode | null): FilterNode | null {\n try {\n return canonicalizeFilter(value, columns);\n } catch {\n return value;\n }\n }\n\n function current(): FilterNode | null {\n try {\n return fromDraft(draft, columns);\n } catch {\n return null;\n }\n }\n\n const emit = (): void => {\n if (timer !== null) clearTimeout(timer);\n timer = null;\n options.onChange(current());\n };\n const flush = (): void => {\n if (timer !== null) emit();\n };\n const emitLater = (): void => {\n if (timer !== null) clearTimeout(timer);\n timer = setTimeout(emit, TEXT_DEBOUNCE_MS);\n };\n\n /** Structural change: new tree, re-render, report at once. */\n const change = (next: DraftNode): void => {\n if (next === draft) return;\n draft = next;\n render();\n emit();\n };\n /** Value typed into an input: no re-render, so the input keeps focus and caret. */\n const setValueQuietly = (path: FilterPath, value: unknown, debounce: boolean): void => {\n draft = updateNode(draft, path, { value });\n if (debounce) emitLater();\n else emit();\n };\n\n const select = (\n cls: string,\n label: string,\n focusId: string,\n items: readonly { value: string; text: string }[],\n selected: string,\n onChange: (value: string) => void,\n ): HTMLButtonElement => {\n const control: SelectControl = createSelect({\n options: items.map(item => ({ value: item.value, label: item.text })),\n value: selected,\n label,\n className: `rs-fb__select ${cls}`,\n onChange,\n });\n control.element.setAttribute('data-fb-focus', focusId);\n return control.element;\n };\n const button = (text: string, cls: string, focusId: string, onClick: () => void, label?: string): HTMLButtonElement => {\n const el: HTMLButtonElement = h('button', { class: `rs-fb__btn ${cls}`, attrs: { type: 'button', 'data-fb-focus': focusId, 'aria-label': label } }, text);\n el.addEventListener('click', onClick);\n return el;\n };\n\n function renderInput(kind: ValueKind, value: unknown, focusId: string, label: string, onValue: (v: unknown) => void): HTMLElement {\n if (kind === 'date' || kind === 'datetime') {\n const date: DateInputControl = createDateInput({\n kind,\n value: typeof value === 'string' && value !== '' ? value : null,\n locale: options.locale ?? 'cs',\n label,\n className: 'rs-fb__date',\n onChange: next => {\n onValue(next ?? undefined);\n flush();\n },\n });\n date.input.setAttribute('data-fb-focus', focusId);\n return date.element;\n }\n const input: HTMLInputElement = h('input', {\n class: 'rs-fb__input',\n attrs: { type: 'text', 'aria-label': label, 'data-fb-focus': focusId, inputmode: kind === 'number' ? 'decimal' : null },\n });\n input.value = formatInput(value, kind);\n input.addEventListener('input', () => onValue(parseInput(input.value, kind)));\n // Leaving the input reports the value at once instead of after the debounce.\n input.addEventListener('change', flush);\n return input;\n }\n\n function lookupItems(column: ColumnDef<unknown>): readonly LookupItem[] {\n return column.lookup ?? [];\n }\n\n /** Checkbox list for in/notIn; values come from the lookup or from loadValues with a search box. */\n function renderList(path: FilterPath, column: ColumnDef<unknown>, value: unknown, focusId: string): HTMLElement {\n const selected: unknown[] = Array.isArray(value) ? [...value] : [];\n const wrapper: HTMLDivElement = h('div', { class: 'rs-fb__list-wrapper' });\n const list: HTMLDivElement = h('div', { class: 'rs-fb__list', attrs: { role: 'group', 'aria-label': messages.value } });\n const isSelected = (v: unknown): boolean => selected.some(s => Object.is(s, v));\n const fill = (items: readonly { value: unknown; text: string }[]): void => {\n // Selected values stay visible even when the current search does not return them.\n const missing: { value: unknown; text: string }[] = selected\n .filter(v => v !== null && !items.some(i => Object.is(i.value, v)))\n .map(v => ({ value: v, text: String(v) }));\n list.replaceChildren(\n ...[...missing, ...items, { value: null, text: messages.blanks }].map((item, index) => {\n const box: HTMLInputElement = h('input', { class: 'rs-fb__check', attrs: { type: 'checkbox', 'data-fb-focus': `${focusId}:${index}` } });\n box.checked = isSelected(item.value);\n box.addEventListener('change', () => {\n const at: number = selected.findIndex(s => Object.is(s, item.value));\n if (box.checked && at < 0) selected.push(item.value);\n if (!box.checked && at >= 0) selected.splice(at, 1);\n setValueQuietly(path, [...selected], false);\n });\n return h('label', { class: 'rs-fb__list-item' }, box, item.text);\n }),\n );\n };\n if (column.lookup) {\n fill(lookupItems(column).map(item => ({ value: item.value, text: item.text })));\n } else if (options.loadValues) {\n const load = options.loadValues;\n const search: HTMLInputElement = h('input', { class: 'rs-fb__input rs-fb__search', attrs: { type: 'search', placeholder: messages.search, 'aria-label': messages.search, 'data-fb-focus': `${focusId}:search` } });\n let searchTimer: ReturnType<typeof setTimeout> | null = null;\n let request: number = 0;\n const run = (text: string): void => {\n const id: number = ++request;\n void load(column.field, text).then(values => {\n if (id !== request || destroyed) return;\n fill(values.filter(v => v !== null && v !== undefined).map(v => ({ value: v, text: String(v) })));\n });\n };\n search.addEventListener('input', () => {\n if (searchTimer !== null) clearTimeout(searchTimer);\n searchTimer = setTimeout(() => run(search.value), TEXT_DEBOUNCE_MS);\n });\n wrapper.append(search);\n fill([]);\n run('');\n } else {\n fill([]);\n }\n wrapper.append(list);\n return wrapper;\n }\n\n function renderValue(path: FilterPath, node: ConditionDraft, column: ColumnDef<unknown> | undefined): HTMLElement {\n const cell: HTMLSpanElement = h('span', { class: 'rs-fb__value' });\n const id: string = pathKey(path);\n const kind: ValueKind = valueKind(column);\n const arity = arityOf(node.op);\n if (!column || arity === 'none') return cell;\n if (arity === 'list') {\n cell.append(renderList(path, column, node.value, `${id}:list`));\n return cell;\n }\n if (kind === 'lookup' || kind === 'boolean') {\n const items: readonly LookupItem[] =\n kind === 'boolean' ? [{ value: true, text: messages.yes }, { value: false, text: messages.no }] : lookupItems(column);\n const index: number = items.findIndex(item => Object.is(item.value, node.value));\n const el: HTMLButtonElement = select(\n 'rs-fb__value-select',\n messages.value,\n `${id}:value`,\n [{ value: '', text: messages.choose }, ...items.map((item, i) => ({ value: String(i), text: item.text }))],\n index < 0 ? '' : String(index),\n value => setValueQuietly(path, value === '' ? undefined : items[Number(value)]?.value, false),\n );\n cell.append(el);\n return cell;\n }\n if (arity === 'pair') {\n const pair: unknown[] = Array.isArray(node.value) ? [...node.value] : [undefined, undefined];\n const setAt = (index: number) => (v: unknown): void => {\n pair[index] = v;\n setValueQuietly(path, [...pair], true);\n };\n cell.append(\n renderInput(kind, pair[0], `${id}:from`, messages.from, setAt(0)),\n h('span', { class: 'rs-fb__dash', attrs: { 'aria-hidden': 'true' } }, '–'),\n renderInput(kind, pair[1], `${id}:to`, messages.to, setAt(1)),\n );\n return cell;\n }\n cell.append(renderInput(kind, node.value, `${id}:value`, messages.value, v => setValueQuietly(path, v, true)));\n return cell;\n }\n\n function renderCondition(path: FilterPath, node: ConditionDraft, level: number): HTMLElement {\n const id: string = pathKey(path);\n const column: ColumnDef<unknown> | undefined = columns.find(c => c.field === node.field);\n const field: HTMLButtonElement = select(\n 'rs-fb__field',\n messages.field,\n `${id}:field`,\n [{ value: '', text: messages.choose }, ...columns.map(c => ({ value: c.field, text: c.caption ?? c.field }))],\n node.field ?? '',\n value => change(updateNode(draft, path, { field: value === '' ? null : value }, columns)),\n );\n const ops: readonly ConditionOp[] = column ? operatorsFor(column) : [];\n const operator: HTMLButtonElement = select(\n 'rs-fb__operator',\n messages.operator,\n `${id}:op`,\n ops.map(op => ({ value: op, text: messages.ops[op] })),\n node.op ?? '',\n value => change(updateNode(draft, path, { op: value as ConditionOp })),\n );\n operator.disabled = !column;\n return h(\n 'div',\n { class: 'rs-fb__condition', attrs: { role: 'treeitem', 'aria-level': level, 'data-path': id } },\n field,\n operator,\n renderValue(path, node, column),\n button('×', 'rs-fb__remove', `${id}:remove`, () => change(removeNode(draft, path)), messages.remove),\n );\n }\n\n function renderGroup(path: FilterPath, node: DraftGroup, level: number): HTMLElement {\n const id: string = pathKey(path);\n const labels: Record<'and' | 'or', string> = node.negated ? { and: messages.nand, or: messages.nor } : { and: messages.and, or: messages.or };\n const ops: HTMLDivElement = h('div', { class: 'rs-fb__ops', attrs: { role: 'group' } });\n for (const op of ['and', 'or'] as const) {\n const el: HTMLButtonElement = button(labels[op], 'rs-fb__op', `${id}:${op}`, () => change(updateNode(draft, path, { op })));\n el.setAttribute('aria-pressed', String(node.op === op));\n ops.append(el);\n }\n const negate: HTMLInputElement = h('input', { class: 'rs-fb__check', attrs: { type: 'checkbox', 'data-fb-focus': `${id}:negate` } });\n negate.checked = node.negated;\n negate.addEventListener('change', () => change(updateNode(draft, path, { negated: negate.checked })));\n const head: HTMLDivElement = h(\n 'div',\n { class: 'rs-fb__group-head' },\n ops,\n h('label', { class: 'rs-fb__negate' }, negate, messages.negate),\n button(messages.addCondition, 'rs-fb__add', `${id}:add-condition`, () => change(addCondition(draft, path))),\n level < MAX_DEPTH ? button(messages.addGroup, 'rs-fb__add', `${id}:add-group`, () => change(addGroup(draft, path))) : null,\n path.length > 0 ? button('×', 'rs-fb__remove', `${id}:remove`, () => change(removeNode(draft, path)), messages.remove) : null,\n );\n const items: HTMLDivElement = h('div', { class: 'rs-fb__items', attrs: { role: 'group' } });\n node.items.forEach((child, index) => {\n const childPath: FilterPath = [...path, index];\n items.append(child.kind === 'group' ? renderGroup(childPath, child, level + 1) : renderCondition(childPath, child, level + 1));\n });\n if (node.items.length === 0) items.append(h('div', { class: 'rs-fb__empty' }, messages.empty));\n return h(\n 'div',\n { class: 'rs-fb__group', attrs: { role: 'treeitem', 'aria-level': level, 'aria-expanded': 'true', 'data-path': id, 'data-negated': node.negated } },\n head,\n items,\n );\n }\n\n function render(): void {\n // Re-rendering replaces the controls; focus returns to the control with the same id.\n const active: string | null = (document.activeElement as HTMLElement | null)?.closest?.('.rs-fb') === root\n ? (document.activeElement?.getAttribute('data-fb-focus') ?? null)\n : null;\n const focused: string | null = pendingFocus ?? active;\n pendingFocus = null;\n closeControlPopover(root);\n root.replaceChildren(draft.kind === 'group' ? renderGroup([], draft, 1) : h('div'));\n if (focused) [...root.querySelectorAll<HTMLElement>('[data-fb-focus]')].find(el => el.getAttribute('data-fb-focus') === focused)?.focus();\n }\n\n render();\n\n return {\n setValue(value: FilterNode | null): void {\n const next: FilterNode | null = canonicalize(value);\n // The same filter keeps the draft, including conditions that are still being filled in.\n if (sameFilter(next, current())) return;\n draft = toDraft(next);\n render();\n },\n flush,\n destroy(): void {\n flush();\n destroyed = true;\n closeControlPopover(root);\n root.remove();\n },\n };\n}\n","type AttrValue = string | number | boolean | null | undefined;\ntype Child = Node | string | null | undefined | false;\n\n/** Creates an element; strings are text nodes, markup is never parsed. */\nexport function h<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n props: { readonly class?: string; readonly attrs?: Readonly<Record<string, AttrValue>> } = {},\n ...children: Child[]\n): HTMLElementTagNameMap[K] {\n const el: HTMLElementTagNameMap[K] = document.createElement(tag);\n if (props.class) el.className = props.class;\n for (const [name, value] of Object.entries(props.attrs ?? {})) {\n if (value === false || value === null || value === undefined) continue;\n el.setAttribute(name, value === true ? '' : String(value));\n }\n for (const child of children) {\n if (child !== null && child !== undefined && child !== false) el.append(child);\n }\n return el;\n}\n","import { messageLanguage } from '@rastr/core';\nimport type { ConditionOp, MessageLanguage } from '@rastr/core';\n\nexport interface FilterBuilderMessages {\n /** Accessible name of the tree. */\n readonly label: string;\n readonly and: string;\n readonly or: string;\n /** Negated \"and\" group (NAND). */\n readonly nand: string;\n /** Negated \"or\" group (NOR). */\n readonly nor: string;\n readonly negate: string;\n readonly addCondition: string;\n readonly addGroup: string;\n readonly remove: string;\n readonly field: string;\n readonly operator: string;\n readonly value: string;\n readonly from: string;\n readonly to: string;\n readonly choose: string;\n readonly yes: string;\n readonly no: string;\n readonly blanks: string;\n readonly search: string;\n readonly empty: string;\n readonly ops: Readonly<Record<ConditionOp, string>>;\n}\n\nconst cs: FilterBuilderMessages = {\n label: 'Filtr',\n and: 'A',\n or: 'Nebo',\n nand: 'Ne a',\n nor: 'Ne nebo',\n negate: 'Negovat',\n addCondition: 'Přidat podmínku',\n addGroup: 'Přidat skupinu',\n remove: 'Odebrat',\n field: 'Pole',\n operator: 'Operátor',\n value: 'Hodnota',\n from: 'Od',\n to: 'Do',\n choose: '(vyberte)',\n yes: 'Ano',\n no: 'Ne',\n blanks: '(Prázdné)',\n search: 'Hledat…',\n empty: 'Žádné podmínky',\n ops: {\n eq: 'Rovná se',\n neq: 'Nerovná se',\n lt: 'Menší než',\n lte: 'Menší nebo rovno',\n gt: 'Větší než',\n gte: 'Větší nebo rovno',\n between: 'Mezi',\n in: 'Je jedna z',\n notIn: 'Není žádná z',\n contains: 'Obsahuje',\n notContains: 'Neobsahuje',\n startsWith: 'Začíná na',\n endsWith: 'Končí na',\n isNull: 'Je prázdné',\n isNotNull: 'Není prázdné',\n },\n};\n\nconst en: FilterBuilderMessages = {\n label: 'Filter',\n and: 'And',\n or: 'Or',\n nand: 'Not and',\n nor: 'Not or',\n negate: 'Negate',\n addCondition: 'Add condition',\n addGroup: 'Add group',\n remove: 'Remove',\n field: 'Field',\n operator: 'Operator',\n value: 'Value',\n from: 'From',\n to: 'To',\n choose: '(choose)',\n yes: 'Yes',\n no: 'No',\n blanks: '(Blanks)',\n search: 'Search…',\n empty: 'No conditions',\n ops: {\n eq: 'Equals',\n neq: 'Does not equal',\n lt: 'Less than',\n lte: 'Less than or equal',\n gt: 'Greater than',\n gte: 'Greater than or equal',\n between: 'Between',\n in: 'Is any of',\n notIn: 'Is none of',\n contains: 'Contains',\n notContains: 'Does not contain',\n startsWith: 'Starts with',\n endsWith: 'Ends with',\n isNull: 'Is blank',\n isNotNull: 'Is not blank',\n },\n};\n\nconst de: FilterBuilderMessages = {\n label: 'Filter',\n and: 'Und',\n or: 'Oder',\n nand: 'Nicht und',\n nor: 'Nicht oder',\n negate: 'Negieren',\n addCondition: 'Bedingung hinzufügen',\n addGroup: 'Gruppe hinzufügen',\n remove: 'Entfernen',\n field: 'Feld',\n operator: 'Operator',\n value: 'Wert',\n from: 'Von',\n to: 'Bis',\n choose: '(auswählen)',\n yes: 'Ja',\n no: 'Nein',\n blanks: '(Leer)',\n search: 'Suchen…',\n empty: 'Keine Bedingungen',\n ops: {\n eq: 'Gleich',\n neq: 'Ungleich',\n lt: 'Kleiner als',\n lte: 'Kleiner oder gleich',\n gt: 'Größer als',\n gte: 'Größer oder gleich',\n between: 'Zwischen',\n in: 'Ist einer von',\n notIn: 'Ist keiner von',\n contains: 'Enthält',\n notContains: 'Enthält nicht',\n startsWith: 'Beginnt mit',\n endsWith: 'Endet mit',\n isNull: 'Ist leer',\n isNotNull: 'Ist nicht leer',\n },\n};\n\nconst pl: FilterBuilderMessages = {\n label: 'Filtr',\n and: 'I',\n or: 'Lub',\n nand: 'Nie i',\n nor: 'Nie lub',\n negate: 'Zaneguj',\n addCondition: 'Dodaj warunek',\n addGroup: 'Dodaj grupę',\n remove: 'Usuń',\n field: 'Pole',\n operator: 'Operator',\n value: 'Wartość',\n from: 'Od',\n to: 'Do',\n choose: '(wybierz)',\n yes: 'Tak',\n no: 'Nie',\n blanks: '(Puste)',\n search: 'Szukaj…',\n empty: 'Brak warunków',\n ops: {\n eq: 'Równa się',\n neq: 'Nie równa się',\n lt: 'Mniejsze niż',\n lte: 'Mniejsze lub równe',\n gt: 'Większe niż',\n gte: 'Większe lub równe',\n between: 'Pomiędzy',\n in: 'Jest jednym z',\n notIn: 'Nie jest żadnym z',\n contains: 'Zawiera',\n notContains: 'Nie zawiera',\n startsWith: 'Zaczyna się od',\n endsWith: 'Kończy się na',\n isNull: 'Jest puste',\n isNotNull: 'Nie jest puste',\n },\n};\n\nconst sk: FilterBuilderMessages = {\n label: 'Filter',\n and: 'A',\n or: 'Alebo',\n nand: 'Nie a',\n nor: 'Nie alebo',\n negate: 'Negovať',\n addCondition: 'Pridať podmienku',\n addGroup: 'Pridať skupinu',\n remove: 'Odstrániť',\n field: 'Pole',\n operator: 'Operátor',\n value: 'Hodnota',\n from: 'Od',\n to: 'Do',\n choose: '(vyberte)',\n yes: 'Áno',\n no: 'Nie',\n blanks: '(Prázdne)',\n search: 'Hľadať…',\n empty: 'Žiadne podmienky',\n ops: {\n eq: 'Rovná sa',\n neq: 'Nerovná sa',\n lt: 'Menšie ako',\n lte: 'Menšie alebo rovné',\n gt: 'Väčšie ako',\n gte: 'Väčšie alebo rovné',\n between: 'Medzi',\n in: 'Je jedna z',\n notIn: 'Nie je žiadna z',\n contains: 'Obsahuje',\n notContains: 'Neobsahuje',\n startsWith: 'Začína na',\n endsWith: 'Končí na',\n isNull: 'Je prázdne',\n isNotNull: 'Nie je prázdne',\n },\n};\n\nexport const FILTER_BUILDER_MESSAGES: Readonly<Record<MessageLanguage, FilterBuilderMessages>> = { cs, en, de, pl, sk };\n\n/** Texts for a locale (\"de-CH\" → German; Czech for languages without texts) with overrides. */\nexport function resolveFilterBuilderMessages(locale: string, overrides?: Partial<FilterBuilderMessages>): FilterBuilderMessages {\n return { ...FILTER_BUILDER_MESSAGES[messageLanguage(locale)], ...overrides };\n}\n"],"mappings":"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","names":["next","items","canonicalizeFilter","canonicalizeFilter"]}
|
package/dist/styles.css
CHANGED
|
@@ -318,6 +318,25 @@
|
|
|
318
318
|
gap: 8px;
|
|
319
319
|
}
|
|
320
320
|
|
|
321
|
+
/* Confirmation before deleting a saved view: narrow, the confirming button in the danger colour. */
|
|
322
|
+
:where(.rs-fb__confirm) {
|
|
323
|
+
width: min(420px, calc(100vw - 32px));
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
:where(.rs-fb__confirm-text) {
|
|
327
|
+
margin: 0;
|
|
328
|
+
color: var(--rs-text);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
:where(.rs-fb__primary.rs-fb__danger) {
|
|
332
|
+
background: var(--rs-danger);
|
|
333
|
+
color: var(--rs-accent-fg);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
:where(.rs-fb__primary.rs-fb__danger:hover) {
|
|
337
|
+
background: color-mix(in oklch, var(--rs-danger) 88%, var(--rs-text));
|
|
338
|
+
}
|
|
339
|
+
|
|
321
340
|
:where(.rs-fb__error) {
|
|
322
341
|
margin: 0;
|
|
323
342
|
color: var(--rs-danger);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rastr/filter-builder",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.3",
|
|
4
4
|
"description": "Visual filter editor for the @rastr/core filter AST.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"dist"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@rastr/
|
|
31
|
-
"@rastr/
|
|
30
|
+
"@rastr/core": "0.1.0-beta.3",
|
|
31
|
+
"@rastr/controls": "0.1.0-beta.3"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@types/node": "^22",
|
|
@@ -39,6 +39,11 @@
|
|
|
39
39
|
"publishConfig": {
|
|
40
40
|
"access": "public"
|
|
41
41
|
},
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "git+https://github.com/oonnddrraass/rastr.git",
|
|
45
|
+
"directory": "packages/filter-builder"
|
|
46
|
+
},
|
|
42
47
|
"scripts": {
|
|
43
48
|
"build": "tsup",
|
|
44
49
|
"test": "vitest run",
|