@aglyn/shared-ui-jsx 1.0.0-beta.230 → 1.0.0-beta.232
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/package.json +5 -5
- package/src/lib/components/copy-field.component.d.ts +25 -0
- package/src/lib/components/copy-field.component.js +77 -0
- package/src/lib/components/copy-field.component.js.map +1 -0
- package/src/lib/components/download-cost-badge.component.d.ts +37 -0
- package/src/lib/components/download-cost-badge.component.js +89 -0
- package/src/lib/components/download-cost-badge.component.js.map +1 -0
- package/src/lib/components/file-drop-zone.component.d.ts +48 -0
- package/src/lib/components/file-drop-zone.component.js +173 -0
- package/src/lib/components/file-drop-zone.component.js.map +1 -0
- package/src/lib/components/list-query-notices.component.d.ts +1 -18
- package/src/lib/components/list-query-notices.component.js +1 -11
- package/src/lib/components/list-query-notices.component.js.map +1 -1
- package/src/lib/components/list-table.component.d.ts +12 -0
- package/src/lib/components/list-table.component.js +30 -4
- package/src/lib/components/list-table.component.js.map +1 -1
- package/src/lib/components/option-card-grid.component.d.ts +46 -0
- package/src/lib/components/option-card-grid.component.js +139 -0
- package/src/lib/components/option-card-grid.component.js.map +1 -0
- package/src/lib/components/searchable-virtual-list.component.d.ts +72 -0
- package/src/lib/components/searchable-virtual-list.component.js +212 -0
- package/src/lib/components/searchable-virtual-list.component.js.map +1 -0
- package/src/lib/components/site-live-notice.component.d.ts +39 -0
- package/src/lib/components/site-live-notice.component.js +95 -0
- package/src/lib/components/site-live-notice.component.js.map +1 -0
- package/src/lib/components/status-chip.component.d.ts +26 -0
- package/src/lib/components/status-chip.component.js +48 -0
- package/src/lib/components/status-chip.component.js.map +1 -0
- package/src/lib/const/list-filter-sentence.d.ts +1 -24
- package/src/lib/const/list-filter-sentence.js +3 -105
- package/src/lib/const/list-filter-sentence.js.map +1 -1
- package/src/lib/const/list-filter.d.ts +1 -219
- package/src/lib/const/list-filter.js +4 -282
- package/src/lib/const/list-filter.js.map +1 -1
- package/src/lib/const/list-grid-filter.d.ts +4 -25
- package/src/lib/const/list-grid-filter.js +20 -44
- package/src/lib/const/list-grid-filter.js.map +1 -1
- package/src/lib/const/list-query-plan.d.ts +1 -154
- package/src/lib/const/list-query-plan.js +4 -610
- package/src/lib/const/list-query-plan.js.map +1 -1
- package/src/lib/hooks/use-client-pagination.d.ts +25 -0
- package/src/lib/hooks/use-client-pagination.js +65 -0
- package/src/lib/hooks/use-client-pagination.js.map +1 -0
- package/src/lib/hooks/use-in-view.d.ts +16 -0
- package/src/lib/hooks/use-in-view.js +66 -0
- package/src/lib/hooks/use-in-view.js.map +1 -0
- package/src/lib/hooks/use-list-column-sort.d.ts +57 -0
- package/src/lib/hooks/use-list-column-sort.js +156 -0
- package/src/lib/hooks/use-list-column-sort.js.map +1 -0
- package/src/lib/hooks/use-viewport-fill.d.ts +39 -0
- package/src/lib/hooks/use-viewport-fill.js +64 -0
- package/src/lib/hooks/use-viewport-fill.js.map +1 -0
|
@@ -15,8 +15,26 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
15
15
|
* See the License for the specific language governing permissions and
|
|
16
16
|
* limitations under the License.
|
|
17
17
|
*/ import { getGridBooleanOperators, getGridDateOperators, getGridNumericOperators, getGridSingleSelectOperators, getGridStringOperators } from "@mui/x-data-grid";
|
|
18
|
-
import {
|
|
19
|
-
|
|
18
|
+
import { listFilterOperators } from "./list-filter.js";
|
|
19
|
+
import { SELECT_TO_GRID } from "@aglyn/shared-util-tools/list-query/list-filter-codecs";
|
|
20
|
+
export { listPlainCodec, listSelectCodec } from "@aglyn/shared-util-tools/list-query/list-filter-codecs";
|
|
21
|
+
/*
|
|
22
|
+
* ONE path from a list's filter clauses to the data grid's own Filters panel
|
|
23
|
+
* and quick search, and back (AGL-3313, shared by every list since AGL-3317).
|
|
24
|
+
*
|
|
25
|
+
* A list stores what it is narrowed by as clauses — `{ field, op, value }`,
|
|
26
|
+
* the shape saved views hold — and edits them through the grid's toolbar
|
|
27
|
+
* rather than controls of its own. What a list declares is its fields, as
|
|
28
|
+
* the `ListFilterField` grammar (`./list-filter`), plus the choices of the
|
|
29
|
+
* fields whose values are picked rather than typed. The grid then offers
|
|
30
|
+
* each as a typed column: a `singleSelect` with its choices where there are
|
|
31
|
+
* choices, the operators the grammar allows otherwise.
|
|
32
|
+
*
|
|
33
|
+
* The clauses keep their stored shape, so a view saved before the panel
|
|
34
|
+
* reads unchanged: the panel shows `equals` as the select's `is` and writes
|
|
35
|
+
* `is` back as `equals`, and a list whose clauses were stored in some other
|
|
36
|
+
* shape says so with a codec.
|
|
37
|
+
*/ const operatorPool = (kind)=>{
|
|
20
38
|
switch(kind){
|
|
21
39
|
case 'boolean':
|
|
22
40
|
return getGridBooleanOperators();
|
|
@@ -78,48 +96,6 @@ const operatorPool = (kind)=>{
|
|
|
78
96
|
}, listFilterColumn(fields, field.column));
|
|
79
97
|
}).filter((column)=>column.filterable);
|
|
80
98
|
}
|
|
81
|
-
/** The grid operator each stored select operator shows as. */ const SELECT_TO_GRID = {
|
|
82
|
-
equals: 'is',
|
|
83
|
-
doesNotEqual: 'not',
|
|
84
|
-
isAnyOf: 'isAnyOf'
|
|
85
|
-
};
|
|
86
|
-
const SELECT_FROM_GRID = {
|
|
87
|
-
is: 'equals',
|
|
88
|
-
not: 'doesNotEqual',
|
|
89
|
-
isAnyOf: 'isAnyOf'
|
|
90
|
-
};
|
|
91
|
-
/** The panel's item as a clause, through the grammar's one notion of "usable". */ const plainClause = (item)=>{
|
|
92
|
-
var _gridFilterRequests_;
|
|
93
|
-
return (_gridFilterRequests_ = gridFilterRequests({
|
|
94
|
-
items: [
|
|
95
|
-
item
|
|
96
|
-
]
|
|
97
|
-
})[0]) != null ? _gridFilterRequests_ : null;
|
|
98
|
-
};
|
|
99
|
-
/** A typed field: the stored operator IS the grid's. */ export const listPlainCodec = {
|
|
100
|
-
toItem: (clause)=>({
|
|
101
|
-
operator: clause.op,
|
|
102
|
-
value: clause.value === '' ? undefined : clause.value
|
|
103
|
-
}),
|
|
104
|
-
toClause: plainClause
|
|
105
|
-
};
|
|
106
|
-
/** A picked field: `equals` shows as `is`, and `isAnyOf` holds a comma list. */ export const listSelectCodec = {
|
|
107
|
-
toItem: (clause)=>{
|
|
108
|
-
const operator = SELECT_TO_GRID[clause.op];
|
|
109
|
-
if (!operator) return null;
|
|
110
|
-
return {
|
|
111
|
-
operator,
|
|
112
|
-
value: operator === 'isAnyOf' ? clause.value.split(',').map((entry)=>entry.trim()).filter(Boolean) : clause.value
|
|
113
|
-
};
|
|
114
|
-
},
|
|
115
|
-
toClause: (item)=>{
|
|
116
|
-
const op = SELECT_FROM_GRID[String(item.operator)];
|
|
117
|
-
const request = op ? plainClause(item) : null;
|
|
118
|
-
return request && op ? _extends({}, request, {
|
|
119
|
-
op
|
|
120
|
-
}) : null;
|
|
121
|
-
}
|
|
122
|
-
};
|
|
123
99
|
/** The select operators a field's allowed clause operators map to. */ export function listSelectOperators(allowed) {
|
|
124
100
|
const shown = allowed.map((op)=>SELECT_TO_GRID[op]).filter(Boolean);
|
|
125
101
|
return getGridSingleSelectOperators().filter((operator)=>shown.includes(operator.value));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../libs/shared/ui/jsx/src/lib/const/list-grid-filter.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n getGridBooleanOperators,\n getGridDateOperators,\n getGridNumericOperators,\n getGridSingleSelectOperators,\n getGridStringOperators,\n type GridColDef,\n type GridFilterItem,\n type GridFilterOperator,\n} from '@mui/x-data-grid'\nimport {\n gridFilterRequests,\n type ListFilterField,\n type ListFilterKind,\n type ListFilterRequest,\n listFilterOperators,\n} from './list-filter'\n\n/*\n * ONE path from a list's filter clauses to the data grid's own Filters panel\n * and quick search, and back (AGL-3313, shared by every list since AGL-3317).\n *\n * A list stores what it is narrowed by as clauses — `{ field, op, value }`,\n * the shape saved views hold — and edits them through the grid's toolbar\n * rather than controls of its own. What a list declares is its fields, as\n * the `ListFilterField` grammar (`./list-filter`), plus the choices of the\n * fields whose values are picked rather than typed. The grid then offers\n * each as a typed column: a `singleSelect` with its choices where there are\n * choices, the operators the grammar allows otherwise.\n *\n * The clauses keep their stored shape, so a view saved before the panel\n * reads unchanged: the panel shows `equals` as the select's `is` and writes\n * `is` back as `equals`, and a list whose clauses were stored in some other\n * shape says so with a codec.\n */\n\n/** One clause a list is narrowed by; `label` names a picked value on a chip. */\nexport interface ListFilterClause extends ListFilterRequest {\n label?: string\n}\n\n/** A choice for a field whose value is picked rather than typed. */\nexport interface ListFilterOption {\n value: string\n label: string\n}\n\nconst operatorPool = (kind: ListFilterKind): GridFilterOperator[] => {\n switch (kind) {\n case 'boolean':\n return getGridBooleanOperators()\n case 'number':\n return getGridNumericOperators()\n case 'date':\n return getGridDateOperators()\n default:\n return getGridStringOperators()\n }\n}\n\n/**\n * The MUI operators for a field — the same list `listFilterOperators` names,\n * resolved to the grid's own operator objects so the panel keeps its native\n * inputs (a date picker for a date, a number field for a number).\n */\nexport function gridFilterOperators(\n field: ListFilterField,\n): GridFilterOperator[] {\n const allowed = listFilterOperators(field)\n return operatorPool(field.kind).filter((operator) =>\n allowed.includes(operator.value),\n )\n}\n\n/**\n * Spread onto a `GridColDef` to make a column filterable exactly as far as the\n * query can serve it. A column with NO declared field is turned off entirely —\n * deliberately, because the alternative is a funnel icon that opens a panel\n * nothing honours.\n */\nexport function listFilterColumn(\n fields: readonly ListFilterField[],\n column: string,\n): { filterable: boolean; filterOperators?: GridFilterOperator[] } {\n const field = fields.find((entry) => entry.column === column)\n if (!field) return { filterable: false }\n const operators = gridFilterOperators(field)\n return operators.length\n ? { filterable: true, filterOperators: operators }\n : { filterable: false }\n}\n\n/**\n * Fields a reader can filter by that are NOT columns on the table.\n *\n * MUI's filter panel lists COLUMNS — `gridFilterableColumnDefinitionsSelector`\n * reads every column definition, hidden ones included — so a filterable field\n * with no column is a field nobody can reach however well the route answers it.\n * Declaring it as a permanently hidden column keeps one source of truth, the\n * field list, instead of a second list of \"extra filters\" that drifts from it.\n *\n * `hideable: false` keeps them out of Manage columns as well: a column with no\n * `renderCell` and no width has nothing to show, and a reader who unhid one\n * would get a strip of blank cells for their trouble.\n *\n * Pair with {@link hiddenFilterVisibility}, which is what actually hides them.\n */\nexport function hiddenFilterColumns(\n fields: readonly ListFilterField[],\n visible: readonly string[],\n headers: Readonly<Record<string, string>> = {},\n): Array<{\n field: string\n headerName: string\n hideable: boolean\n filterable: boolean\n filterOperators?: GridFilterOperator[]\n}> {\n return fields\n .filter((field) => !visible.includes(field.column))\n .map((field) => ({\n field: field.column,\n headerName: headers[field.column] ?? field.column,\n hideable: false,\n ...listFilterColumn(fields, field.column),\n }))\n .filter((column) => column.filterable)\n}\n\n/** The grid operator each stored select operator shows as. */\nconst SELECT_TO_GRID: Readonly<Record<string, string>> = {\n equals: 'is',\n doesNotEqual: 'not',\n isAnyOf: 'isAnyOf',\n}\nconst SELECT_FROM_GRID: Readonly<Record<string, string>> = {\n is: 'equals',\n not: 'doesNotEqual',\n isAnyOf: 'isAnyOf',\n}\n\n/**\n * How one field's stored clause and the panel's item translate. A field\n * with choices uses {@link listSelectCodec}; a typed one,\n * {@link listPlainCodec}. A list whose clauses predate the panel supplies\n * its own.\n */\nexport interface ListGridFilterCodec {\n toItem: (clause: ListFilterClause) => Pick<GridFilterItem, 'operator' | 'value'> | null\n toClause: (item: GridFilterItem) => ListFilterClause | null\n}\n\n/** The panel's item as a clause, through the grammar's one notion of \"usable\". */\nconst plainClause = (item: GridFilterItem): ListFilterClause | null =>\n gridFilterRequests({ items: [item] })[0] ?? null\n\n/** A typed field: the stored operator IS the grid's. */\nexport const listPlainCodec: ListGridFilterCodec = {\n toItem: (clause) => ({\n operator: clause.op,\n value: clause.value === '' ? undefined : clause.value,\n }),\n toClause: plainClause,\n}\n\n/** A picked field: `equals` shows as `is`, and `isAnyOf` holds a comma list. */\nexport const listSelectCodec: ListGridFilterCodec = {\n toItem: (clause) => {\n const operator = SELECT_TO_GRID[clause.op]\n if (!operator) return null\n return {\n operator,\n value:\n operator === 'isAnyOf'\n ? clause.value.split(',').map((entry) => entry.trim()).filter(Boolean)\n : clause.value,\n }\n },\n toClause: (item) => {\n const op = SELECT_FROM_GRID[String(item.operator)]\n const request = op ? plainClause(item) : null\n return request && op ? { ...request, op } : null\n },\n}\n\n/** The select operators a field's allowed clause operators map to. */\nexport function listSelectOperators(allowed: readonly string[]) {\n const shown = allowed.map((op) => SELECT_TO_GRID[op]).filter(Boolean)\n return getGridSingleSelectOperators().filter((operator) =>\n shown.includes(operator.value),\n )\n}\n\n/**\n * The list's columns, each made filterable exactly as far as its field is\n * declared, and every declared field that is not a column added as a\n * permanently hidden one — the panel lists columns, so a field with none\n * could not be reached (`hiddenFilterColumns`).\n *\n * A field with choices becomes a `singleSelect` over them, whatever the\n * column rendered before; one without keeps the grammar's operators; a\n * column no field declares offers no filter, because the list, not the\n * grid, answers every filter and would have nothing to answer it with.\n */\nexport function listFilterGridColumns(\n columns: readonly GridColDef[],\n fields: readonly ListFilterField[],\n options: Readonly<Record<string, readonly ListFilterOption[]>> = {},\n headers: Readonly<Record<string, string>> = {},\n): GridColDef[] {\n const selectProps = (field: ListFilterField) => {\n const choices = options[field.column] ?? []\n const operators = listSelectOperators(listFilterOperators(field))\n return {\n type: 'singleSelect' as const,\n valueOptions: choices.map((choice) => ({ value: choice.value, label: choice.label })),\n filterable: choices.length > 0 && operators.length > 0,\n filterOperators: operators,\n }\n }\n /*\n * A column made a select keeps what it DREW. The grid formats a\n * `singleSelect` cell as the label of the option its value matches, and as\n * nothing when none does — so a column whose `valueGetter` hands back a\n * label (an address for an actor id, a site name for a scope id) went\n * blank the moment it gained a filter (AGL-3321). A column with its own\n * `renderCell` is left alone; any other draws the matching option's label\n * when there is one, and its own value when there is not.\n */\n const keepsItsCell = (column: GridColDef, field: ListFilterField) => {\n if (column.renderCell) return {}\n const labels = new Map(\n (options[field.column] ?? []).map((choice) => [choice.value, choice.label]),\n )\n return {\n renderCell: (params: { value?: unknown }) => {\n const value = params.value\n if (value === undefined || value === null) return ''\n return labels.get(String(value)) ?? String(value)\n },\n }\n }\n const shown = columns.map((column): GridColDef => {\n if (column.filterable === false && column.hideable === false) return column\n const field = fields.find((entry) => entry.column === column.field)\n if (!field) return { ...column, filterable: false }\n if (options[field.column]) {\n return { ...column, ...selectProps(field), ...keepsItsCell(column, field) } as GridColDef\n }\n return { ...column, ...listFilterColumn(fields, field.column) }\n })\n const present = columns.map((column) => column.field)\n /*\n * A field with choices is judged filterable by its choices, not by the\n * grid's operators for its kind: a Yes/No `boolean` field allows `equals`,\n * which the grid's boolean pool names `is`, so judged the plain way it has\n * no operator and was dropped before its choices could make it a select —\n * leaving a served filter nobody could reach (AGL-3332's Clicked). A field\n * with choices stays a column even while they are empty (products not yet\n * loaded): the list still reads a clause on it back through that column.\n */\n const hidden = fields\n .filter((field) => !present.includes(field.column))\n .flatMap((field): GridColDef[] => {\n const base = {\n field: field.column,\n headerName: headers[field.column] ?? field.column,\n hideable: false,\n }\n if (options[field.column]) return [{ ...base, ...selectProps(field) } as GridColDef]\n const plain = listFilterColumn(fields, field.column)\n return plain.filterable ? [{ ...base, ...plain } as GridColDef] : []\n })\n return [...shown, ...hidden]\n}\n\n/**\n * Whether a row answers a quick search: every whitespace-separated word\n * appears, case-insensitively, in SOME of the row's searched values. A\n * blank search matches every row, so an emptied box is the list unfiltered.\n * An array value (tags) is searched member by member.\n */\nexport function listRowMatchesSearch(\n row: object,\n paths: readonly string[],\n words: readonly string[],\n): boolean {\n const asked = words.map((word) => word.trim().toLowerCase()).filter(Boolean)\n if (!asked.length) return true\n const values = paths\n .flatMap((path) => {\n const value = path\n .split('.')\n .reduce<unknown>(\n (at, key) => (at == null ? undefined : (at as Record<string, unknown>)[key]),\n row,\n )\n return Array.isArray(value) ? value : [value]\n })\n .filter((value): value is string | number => typeof value === 'string' || typeof value === 'number')\n .map((value) => String(value).toLowerCase())\n .filter(Boolean)\n return asked.every((word) => values.some((value) => value.includes(word)))\n}\n\n/**\n * The clauses with one field's clause set: replaced in place where the field\n * already had one, appended where it did not, removed when `next` is null.\n * A field holds ONE clause, which is what the panel edits.\n */\nexport function upsertListFilterClause<Clause extends ListFilterClause>(\n clauses: readonly Clause[],\n field: string,\n next: Clause | null,\n): Clause[] {\n const at = clauses.findIndex((clause) => clause.field === field)\n const rest = clauses.filter((clause) => clause.field !== field)\n if (!next) return rest\n if (at === -1) return [...rest, next]\n return [...rest.slice(0, at), next, ...rest.slice(at)]\n}\n\nexport { listFilterClauseSentence } from './list-filter-sentence'\n"],"names":["getGridBooleanOperators","getGridDateOperators","getGridNumericOperators","getGridSingleSelectOperators","getGridStringOperators","gridFilterRequests","listFilterOperators","operatorPool","kind","gridFilterOperators","field","allowed","filter","operator","includes","value","listFilterColumn","fields","column","find","entry","filterable","operators","length","filterOperators","hiddenFilterColumns","visible","headers","map","headerName","hideable","SELECT_TO_GRID","equals","doesNotEqual","isAnyOf","SELECT_FROM_GRID","is","not","plainClause","item","items","listPlainCodec","toItem","clause","op","undefined","toClause","listSelectCodec","split","trim","Boolean","String","request","listSelectOperators","shown","listFilterGridColumns","columns","options","selectProps","choices","type","valueOptions","choice","label","keepsItsCell","renderCell","labels","Map","params","get","present","hidden","flatMap","base","plain","listRowMatchesSearch","row","paths","words","asked","word","toLowerCase","values","path","reduce","at","key","Array","isArray","every","some","upsertListFilterClause","clauses","next","findIndex","rest","slice","listFilterClauseSentence"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/shared/ui/jsx/src/lib/const/list-grid-filter.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n getGridBooleanOperators,\n getGridDateOperators,\n getGridNumericOperators,\n getGridSingleSelectOperators,\n getGridStringOperators,\n type GridColDef,\n type GridFilterOperator,\n} from '@mui/x-data-grid'\nimport {\n type ListFilterField,\n type ListFilterKind,\n listFilterOperators,\n} from './list-filter'\nimport {\n type ListFilterClause,\n type ListFilterOption,\n SELECT_TO_GRID,\n} from '@aglyn/shared-util-tools/list-query/list-filter-codecs'\n\nexport {\n type ListFilterClause,\n type ListFilterOption,\n type ListGridFilterCodec,\n listPlainCodec,\n listSelectCodec,\n} from '@aglyn/shared-util-tools/list-query/list-filter-codecs'\n\n/*\n * ONE path from a list's filter clauses to the data grid's own Filters panel\n * and quick search, and back (AGL-3313, shared by every list since AGL-3317).\n *\n * A list stores what it is narrowed by as clauses — `{ field, op, value }`,\n * the shape saved views hold — and edits them through the grid's toolbar\n * rather than controls of its own. What a list declares is its fields, as\n * the `ListFilterField` grammar (`./list-filter`), plus the choices of the\n * fields whose values are picked rather than typed. The grid then offers\n * each as a typed column: a `singleSelect` with its choices where there are\n * choices, the operators the grammar allows otherwise.\n *\n * The clauses keep their stored shape, so a view saved before the panel\n * reads unchanged: the panel shows `equals` as the select's `is` and writes\n * `is` back as `equals`, and a list whose clauses were stored in some other\n * shape says so with a codec.\n */\n\nconst operatorPool = (kind: ListFilterKind): GridFilterOperator[] => {\n switch (kind) {\n case 'boolean':\n return getGridBooleanOperators()\n case 'number':\n return getGridNumericOperators()\n case 'date':\n return getGridDateOperators()\n default:\n return getGridStringOperators()\n }\n}\n\n/**\n * The MUI operators for a field — the same list `listFilterOperators` names,\n * resolved to the grid's own operator objects so the panel keeps its native\n * inputs (a date picker for a date, a number field for a number).\n */\nexport function gridFilterOperators(\n field: ListFilterField,\n): GridFilterOperator[] {\n const allowed = listFilterOperators(field)\n return operatorPool(field.kind).filter((operator) =>\n allowed.includes(operator.value),\n )\n}\n\n/**\n * Spread onto a `GridColDef` to make a column filterable exactly as far as the\n * query can serve it. A column with NO declared field is turned off entirely —\n * deliberately, because the alternative is a funnel icon that opens a panel\n * nothing honours.\n */\nexport function listFilterColumn(\n fields: readonly ListFilterField[],\n column: string,\n): { filterable: boolean; filterOperators?: GridFilterOperator[] } {\n const field = fields.find((entry) => entry.column === column)\n if (!field) return { filterable: false }\n const operators = gridFilterOperators(field)\n return operators.length\n ? { filterable: true, filterOperators: operators }\n : { filterable: false }\n}\n\n/**\n * Fields a reader can filter by that are NOT columns on the table.\n *\n * MUI's filter panel lists COLUMNS — `gridFilterableColumnDefinitionsSelector`\n * reads every column definition, hidden ones included — so a filterable field\n * with no column is a field nobody can reach however well the route answers it.\n * Declaring it as a permanently hidden column keeps one source of truth, the\n * field list, instead of a second list of \"extra filters\" that drifts from it.\n *\n * `hideable: false` keeps them out of Manage columns as well: a column with no\n * `renderCell` and no width has nothing to show, and a reader who unhid one\n * would get a strip of blank cells for their trouble.\n *\n * Pair with {@link hiddenFilterVisibility}, which is what actually hides them.\n */\nexport function hiddenFilterColumns(\n fields: readonly ListFilterField[],\n visible: readonly string[],\n headers: Readonly<Record<string, string>> = {},\n): Array<{\n field: string\n headerName: string\n hideable: boolean\n filterable: boolean\n filterOperators?: GridFilterOperator[]\n}> {\n return fields\n .filter((field) => !visible.includes(field.column))\n .map((field) => ({\n field: field.column,\n headerName: headers[field.column] ?? field.column,\n hideable: false,\n ...listFilterColumn(fields, field.column),\n }))\n .filter((column) => column.filterable)\n}\n\n/** The select operators a field's allowed clause operators map to. */\nexport function listSelectOperators(allowed: readonly string[]) {\n const shown = allowed.map((op) => SELECT_TO_GRID[op]).filter(Boolean)\n return getGridSingleSelectOperators().filter((operator) =>\n shown.includes(operator.value),\n )\n}\n\n/**\n * The list's columns, each made filterable exactly as far as its field is\n * declared, and every declared field that is not a column added as a\n * permanently hidden one — the panel lists columns, so a field with none\n * could not be reached (`hiddenFilterColumns`).\n *\n * A field with choices becomes a `singleSelect` over them, whatever the\n * column rendered before; one without keeps the grammar's operators; a\n * column no field declares offers no filter, because the list, not the\n * grid, answers every filter and would have nothing to answer it with.\n */\nexport function listFilterGridColumns(\n columns: readonly GridColDef[],\n fields: readonly ListFilterField[],\n options: Readonly<Record<string, readonly ListFilterOption[]>> = {},\n headers: Readonly<Record<string, string>> = {},\n): GridColDef[] {\n const selectProps = (field: ListFilterField) => {\n const choices = options[field.column] ?? []\n const operators = listSelectOperators(listFilterOperators(field))\n return {\n type: 'singleSelect' as const,\n valueOptions: choices.map((choice) => ({ value: choice.value, label: choice.label })),\n filterable: choices.length > 0 && operators.length > 0,\n filterOperators: operators,\n }\n }\n /*\n * A column made a select keeps what it DREW. The grid formats a\n * `singleSelect` cell as the label of the option its value matches, and as\n * nothing when none does — so a column whose `valueGetter` hands back a\n * label (an address for an actor id, a site name for a scope id) went\n * blank the moment it gained a filter (AGL-3321). A column with its own\n * `renderCell` is left alone; any other draws the matching option's label\n * when there is one, and its own value when there is not.\n */\n const keepsItsCell = (column: GridColDef, field: ListFilterField) => {\n if (column.renderCell) return {}\n const labels = new Map(\n (options[field.column] ?? []).map((choice) => [choice.value, choice.label]),\n )\n return {\n renderCell: (params: { value?: unknown }) => {\n const value = params.value\n if (value === undefined || value === null) return ''\n return labels.get(String(value)) ?? String(value)\n },\n }\n }\n const shown = columns.map((column): GridColDef => {\n if (column.filterable === false && column.hideable === false) return column\n const field = fields.find((entry) => entry.column === column.field)\n if (!field) return { ...column, filterable: false }\n if (options[field.column]) {\n return { ...column, ...selectProps(field), ...keepsItsCell(column, field) } as GridColDef\n }\n return { ...column, ...listFilterColumn(fields, field.column) }\n })\n const present = columns.map((column) => column.field)\n /*\n * A field with choices is judged filterable by its choices, not by the\n * grid's operators for its kind: a Yes/No `boolean` field allows `equals`,\n * which the grid's boolean pool names `is`, so judged the plain way it has\n * no operator and was dropped before its choices could make it a select —\n * leaving a served filter nobody could reach (AGL-3332's Clicked). A field\n * with choices stays a column even while they are empty (products not yet\n * loaded): the list still reads a clause on it back through that column.\n */\n const hidden = fields\n .filter((field) => !present.includes(field.column))\n .flatMap((field): GridColDef[] => {\n const base = {\n field: field.column,\n headerName: headers[field.column] ?? field.column,\n hideable: false,\n }\n if (options[field.column]) return [{ ...base, ...selectProps(field) } as GridColDef]\n const plain = listFilterColumn(fields, field.column)\n return plain.filterable ? [{ ...base, ...plain } as GridColDef] : []\n })\n return [...shown, ...hidden]\n}\n\n/**\n * Whether a row answers a quick search: every whitespace-separated word\n * appears, case-insensitively, in SOME of the row's searched values. A\n * blank search matches every row, so an emptied box is the list unfiltered.\n * An array value (tags) is searched member by member.\n */\nexport function listRowMatchesSearch(\n row: object,\n paths: readonly string[],\n words: readonly string[],\n): boolean {\n const asked = words.map((word) => word.trim().toLowerCase()).filter(Boolean)\n if (!asked.length) return true\n const values = paths\n .flatMap((path) => {\n const value = path\n .split('.')\n .reduce<unknown>(\n (at, key) => (at == null ? undefined : (at as Record<string, unknown>)[key]),\n row,\n )\n return Array.isArray(value) ? value : [value]\n })\n .filter((value): value is string | number => typeof value === 'string' || typeof value === 'number')\n .map((value) => String(value).toLowerCase())\n .filter(Boolean)\n return asked.every((word) => values.some((value) => value.includes(word)))\n}\n\n/**\n * The clauses with one field's clause set: replaced in place where the field\n * already had one, appended where it did not, removed when `next` is null.\n * A field holds ONE clause, which is what the panel edits.\n */\nexport function upsertListFilterClause<Clause extends ListFilterClause>(\n clauses: readonly Clause[],\n field: string,\n next: Clause | null,\n): Clause[] {\n const at = clauses.findIndex((clause) => clause.field === field)\n const rest = clauses.filter((clause) => clause.field !== field)\n if (!next) return rest\n if (at === -1) return [...rest, next]\n return [...rest.slice(0, at), next, ...rest.slice(at)]\n}\n\nexport { listFilterClauseSentence } from './list-filter-sentence'\n"],"names":["getGridBooleanOperators","getGridDateOperators","getGridNumericOperators","getGridSingleSelectOperators","getGridStringOperators","listFilterOperators","SELECT_TO_GRID","listPlainCodec","listSelectCodec","operatorPool","kind","gridFilterOperators","field","allowed","filter","operator","includes","value","listFilterColumn","fields","column","find","entry","filterable","operators","length","filterOperators","hiddenFilterColumns","visible","headers","map","headerName","hideable","listSelectOperators","shown","op","Boolean","listFilterGridColumns","columns","options","selectProps","choices","type","valueOptions","choice","label","keepsItsCell","renderCell","labels","Map","params","undefined","get","String","present","hidden","flatMap","base","plain","listRowMatchesSearch","row","paths","words","asked","word","trim","toLowerCase","values","path","split","reduce","at","key","Array","isArray","every","some","upsertListFilterClause","clauses","next","findIndex","clause","rest","slice","listFilterClauseSentence"],"mappings":"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"}
|
|
@@ -14,157 +14,4 @@
|
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* How the written search fields were normalized, so a typed value becomes the
|
|
20
|
-
* key the query can match: the lower-cased key (`equals`, `startsWith`), the
|
|
21
|
-
* one word-prefix token (`contains`, search), and the reversed key
|
|
22
|
-
* (`endsWith`). `maxPrefix` is the longest token a writer stores.
|
|
23
|
-
*/
|
|
24
|
-
export interface ListQueryNormalizers {
|
|
25
|
-
key(value: string): string;
|
|
26
|
-
token(value: string): string;
|
|
27
|
-
reversed(value: string): string;
|
|
28
|
-
maxPrefix: number;
|
|
29
|
-
}
|
|
30
|
-
/** A predicate on the query, SDK-free. `path` `__name__` is the document id. */
|
|
31
|
-
export type ListQueryOp = '==' | '!=' | '<' | '<=' | '>' | '>=' | 'in' | 'array-contains' | 'array-contains-any';
|
|
32
|
-
export type ListQueryValue = string | number | boolean | null | Date | readonly (string | number)[];
|
|
33
|
-
export interface ListQueryFilter {
|
|
34
|
-
path: string;
|
|
35
|
-
op: ListQueryOp;
|
|
36
|
-
value: ListQueryValue;
|
|
37
|
-
}
|
|
38
|
-
export interface ListQuerySort {
|
|
39
|
-
path: string;
|
|
40
|
-
direction: 'asc' | 'desc';
|
|
41
|
-
/** The grid column this order sorts, when the list lets the grid sort by it. */
|
|
42
|
-
column?: string;
|
|
43
|
-
}
|
|
44
|
-
/** The document id, as a path. */
|
|
45
|
-
export declare const LIST_QUERY_ID_PATH = "__name__";
|
|
46
|
-
/** Firestore's cap on disjunctions — `in` values times `array-contains-any` values. */
|
|
47
|
-
export declare const LIST_QUERY_DISJUNCTIONS = 30;
|
|
48
|
-
export interface ListQueryDeclaration {
|
|
49
|
-
/** Every field the Filters panel may name. */
|
|
50
|
-
fields: readonly ListFilterField[];
|
|
51
|
-
/**
|
|
52
|
-
* The orders the list offers, its default FIRST. The query takes no other:
|
|
53
|
-
* each is a composite the index file carries for every filterable field.
|
|
54
|
-
*/
|
|
55
|
-
sorts: readonly ListQuerySort[];
|
|
56
|
-
/**
|
|
57
|
-
* The quick search: the token array every write stamps (see
|
|
58
|
-
* `nameSearchTokens`), queried with `array-contains` on one typed word.
|
|
59
|
-
* Absent, the list offers no search box.
|
|
60
|
-
*/
|
|
61
|
-
search?: {
|
|
62
|
-
tokensPath: string;
|
|
63
|
-
/**
|
|
64
|
-
* Under a scope clause the search cannot be a SECOND array clause, so a
|
|
65
|
-
* scoped list writes `<scopeToken><join><prefix>` tokens and the query
|
|
66
|
-
* asks `array-contains-any` over the scope's tokens joined to the word —
|
|
67
|
-
* one array clause that answers both.
|
|
68
|
-
*
|
|
69
|
-
* ⛔ Rules that grant a list from `visibleTo.hasAny(...)` DENY this
|
|
70
|
-
* query, because it no longer constrains `visibleTo`. Declare it only for
|
|
71
|
-
* readers whose rule needs no `visibleTo` term, or for an Admin-SDK read;
|
|
72
|
-
* see `SCOPED_SEARCH_JOIN` in `@aglyn/aglyn/app-utils/name-search`.
|
|
73
|
-
*/
|
|
74
|
-
scoped?: {
|
|
75
|
-
tokensPath: string;
|
|
76
|
-
join: string;
|
|
77
|
-
};
|
|
78
|
-
};
|
|
79
|
-
/**
|
|
80
|
-
* Keys a presence-map field (`keysOf`) is filtered by, for the index
|
|
81
|
-
* enumeration: `sources.form`, `sources.order`, …
|
|
82
|
-
*/
|
|
83
|
-
keys?: Readonly<Record<string, readonly string[]>>;
|
|
84
|
-
}
|
|
85
|
-
/** What the list asks this time. */
|
|
86
|
-
export interface ListQueryRequest {
|
|
87
|
-
clauses: readonly ListFilterRequest[];
|
|
88
|
-
/** The quick search's words. */
|
|
89
|
-
search?: readonly string[];
|
|
90
|
-
/** The order asked for; a sort the declaration does not offer is ignored. */
|
|
91
|
-
sort?: ListQuerySort | null;
|
|
92
|
-
/**
|
|
93
|
-
* Predicates the list always applies — its scope — which count against
|
|
94
|
-
* the same limits. An `array-contains-any` scope is what a scoped search
|
|
95
|
-
* folds into (`declaration.search.scoped`).
|
|
96
|
-
*/
|
|
97
|
-
base?: readonly ListQueryFilter[];
|
|
98
|
-
}
|
|
99
|
-
export interface ListQueryRefusal {
|
|
100
|
-
/** The clause refused, or `search`. */
|
|
101
|
-
clause: ListFilterRequest | 'search';
|
|
102
|
-
reason: string;
|
|
103
|
-
}
|
|
104
|
-
export interface ListQueryPlan {
|
|
105
|
-
/** Every predicate, base included, in the order the SDK should add them. */
|
|
106
|
-
filters: ListQueryFilter[];
|
|
107
|
-
/** The one order: the range field's when there is one, else the sort. */
|
|
108
|
-
orderBy: ListQuerySort;
|
|
109
|
-
/** The clauses on the query. */
|
|
110
|
-
served: ListFilterRequest[];
|
|
111
|
-
/** The search word on the query, or null. */
|
|
112
|
-
searched: string | null;
|
|
113
|
-
/** What was asked and could not be put on the query, with why. */
|
|
114
|
-
refused: ListQueryRefusal[];
|
|
115
|
-
/** Said to the reader about what WAS served — e.g. one search word of two. */
|
|
116
|
-
notices: string[];
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Every clause and the search, as ONE query — or as much of it as one query
|
|
120
|
-
* can hold, with the rest refused by name.
|
|
121
|
-
*
|
|
122
|
-
* Order of claim: the base (the list's scope) first, then the search, then
|
|
123
|
-
* the clauses in the order they were added. A clause that would be a second
|
|
124
|
-
* array clause, a second inequality field or push the disjunctions past
|
|
125
|
-
* thirty is refused, and the list shows the refusal beside its chip; the
|
|
126
|
-
* clauses before it stand. The search is claimed before the clauses because
|
|
127
|
-
* it is the thing a reader typed last and looks at.
|
|
128
|
-
*/
|
|
129
|
-
export declare function planListQuery(declaration: ListQueryDeclaration, request: ListQueryRequest, names: ListQueryNormalizers): ListQueryPlan;
|
|
130
|
-
/** A composite the plan's shapes need, in the index file's own terms. */
|
|
131
|
-
export interface ListQueryIndex {
|
|
132
|
-
fields: Array<{
|
|
133
|
-
fieldPath: string;
|
|
134
|
-
order?: 'ASCENDING' | 'DESCENDING';
|
|
135
|
-
arrayConfig?: 'CONTAINS';
|
|
136
|
-
}>;
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Every composite a declaration's queries can need, under index merging:
|
|
140
|
-
* one `(predicate field, order)` index per equality or array field and per
|
|
141
|
-
* order the list can take — its sorts, and the order each range imposes.
|
|
142
|
-
*
|
|
143
|
-
* `base` names the scope predicates the list always applies (their paths
|
|
144
|
-
* and whether they are array clauses), which need their own index per order
|
|
145
|
-
* like any other equality. Pinned by each list's spec against
|
|
146
|
-
* `cloud/firebase-firestore.indexes.json`.
|
|
147
|
-
*/
|
|
148
|
-
export declare function listQueryIndexes(declaration: ListQueryDeclaration, base?: readonly {
|
|
149
|
-
path: string;
|
|
150
|
-
array?: boolean;
|
|
151
|
-
}[]): ListQueryIndex[];
|
|
152
|
-
/** An index entry as `cloud/firebase-firestore.indexes.json` stores it. */
|
|
153
|
-
interface IndexFileEntry {
|
|
154
|
-
collectionGroup: string;
|
|
155
|
-
queryScope: string;
|
|
156
|
-
fields: Array<{
|
|
157
|
-
fieldPath: string;
|
|
158
|
-
order?: string;
|
|
159
|
-
arrayConfig?: string;
|
|
160
|
-
}>;
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* The composites a declaration needs that an index file does not hold — what
|
|
164
|
-
* a list's spec asserts is empty, so a declared filter cannot ship without
|
|
165
|
-
* the index that serves it.
|
|
166
|
-
*/
|
|
167
|
-
export declare function missingListQueryIndexes(file: {
|
|
168
|
-
indexes: readonly IndexFileEntry[];
|
|
169
|
-
}, collectionGroup: string, needed: readonly ListQueryIndex[], queryScope?: 'COLLECTION' | 'COLLECTION_GROUP'): ListQueryIndex[];
|
|
170
|
-
export {};
|
|
17
|
+
export * from '@aglyn/shared-util-tools/list-query/list-query-plan';
|