@standardbeagle/edit-db 0.4.494 → 0.4.583
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/common/saved-objects.d.ts +40 -0
- package/dist/common/schema-queries.d.ts +1 -0
- package/dist/components/bulk-edit-dialog.d.ts +18 -0
- package/dist/components/content-viewer.d.ts +8 -1
- package/dist/components/data-table.d.ts +49 -2
- package/dist/components/detail-panel.d.ts +2 -2
- package/dist/components/export-button.d.ts +19 -0
- package/dist/components/export-button.test.d.ts +1 -0
- package/dist/components/filters/number-filter.d.ts +8 -1
- package/dist/components/fk-cell-popover.d.ts +5 -5
- package/dist/components/form-runner-host.d.ts +14 -0
- package/dist/components/form-runner.d.ts +43 -0
- package/dist/components/form-subform.d.ts +11 -0
- package/dist/components/form-subform.test.d.ts +0 -0
- package/dist/components/grouped-grid-summary.d.ts +17 -0
- package/dist/components/grouped-grid-summary.test.d.ts +0 -0
- package/dist/components/m2m-panel.d.ts +4 -5
- package/dist/components/ui/table.test.d.ts +1 -0
- package/dist/data-edit.d.ts +17 -4
- package/dist/data-panel.d.ts +0 -11
- package/dist/edit-db.css +1 -1
- package/dist/editor.d.ts +22 -1
- package/dist/editor.es.js +6822 -4695
- package/dist/editor.stories.d.ts +0 -1
- package/dist/editor.umd.cjs +41 -27
- package/dist/hooks/useColumnNav.d.ts +3 -3
- package/dist/hooks/useColumnSizingPersistence.d.ts +19 -0
- package/dist/hooks/useColumnVisibilityPersistence.d.ts +34 -0
- package/dist/hooks/useColumnVisibilityPersistence.test.d.ts +1 -0
- package/dist/hooks/useCompositeTableRef.d.ts +2 -2
- package/dist/hooks/useDataTable.d.ts +46 -5
- package/dist/hooks/useDebouncedCommit.test.d.ts +1 -0
- package/dist/hooks/useDeltaMutation.d.ts +20 -0
- package/dist/hooks/useDeltaMutation.test.d.ts +0 -0
- package/dist/hooks/useEditorConfig.d.ts +27 -0
- package/dist/hooks/useFitMode.d.ts +23 -0
- package/dist/hooks/useFitMode.test.d.ts +1 -0
- package/dist/hooks/usePanelSize.d.ts +28 -0
- package/dist/hooks/usePanelSize.test.d.ts +1 -0
- package/dist/hooks/usePath.d.ts +1 -0
- package/dist/hooks/useRowHoverActions.d.ts +26 -0
- package/dist/hooks/useSchema.d.ts +8 -2
- package/dist/hooks/useSchemaFilter.test.d.ts +1 -0
- package/dist/hooks/useTableExport.d.ts +13 -0
- package/dist/hooks/useTableMutation.d.ts +1 -4
- package/dist/hooks/useTableRef.d.ts +3 -3
- package/dist/hooks/useTableStats.d.ts +0 -24
- package/dist/index.d.ts +13 -1
- package/dist/lib/column-order.d.ts +17 -0
- package/dist/lib/column-order.test.d.ts +1 -0
- package/dist/lib/content-detect.d.ts +34 -0
- package/dist/lib/content-detect.sniff.test.d.ts +1 -0
- package/dist/lib/drill-stack.d.ts +19 -2
- package/dist/lib/export.d.ts +122 -0
- package/dist/lib/export.test.d.ts +1 -0
- package/dist/lib/field-validation.d.ts +16 -0
- package/dist/lib/fk.d.ts +13 -0
- package/dist/lib/form-definition.d.ts +57 -0
- package/dist/lib/form-definition.test.d.ts +1 -0
- package/dist/lib/form-runner-nav.d.ts +19 -0
- package/dist/lib/form-runner-nav.test.d.ts +1 -0
- package/dist/lib/form-subform.d.ts +12 -0
- package/dist/lib/form-subform.test.d.ts +1 -0
- package/dist/lib/form-widget.d.ts +36 -0
- package/dist/lib/form-widget.test.d.ts +1 -0
- package/dist/lib/format-value.d.ts +26 -0
- package/dist/lib/grid-grouping.d.ts +63 -0
- package/dist/lib/grid-grouping.test.d.ts +1 -0
- package/dist/lib/m2m.d.ts +26 -1
- package/dist/lib/mutation-payload.d.ts +37 -0
- package/dist/lib/mutation-payload.test.d.ts +1 -0
- package/dist/lib/query-builder.d.ts +116 -9
- package/dist/lib/row-id.d.ts +11 -0
- package/dist/lib/rows-by-pk-query.test.d.ts +1 -0
- package/dist/lib/scalar-types.d.ts +30 -0
- package/dist/lib/scalar-types.test.d.ts +1 -0
- package/dist/lib/table-action.d.ts +24 -0
- package/dist/{tableList.d.ts → table-list.d.ts} +1 -1
- package/dist/table-list.test.d.ts +0 -0
- package/dist/table-view.chart.test.d.ts +1 -0
- package/dist/table-view.d.ts +22 -0
- package/dist/table-view.test.d.ts +0 -0
- package/dist/types/schema.d.ts +45 -10
- package/package.json +2 -2
- package/dist/common/schema.d.ts +0 -2
- package/dist/data-data-table.d.ts +0 -17
- /package/dist/common/{schema.test.d.ts → saved-objects.test.d.ts} +0 -0
- /package/dist/{components/filters/use-debounced-commit.test.d.ts → common/schema-queries.test.d.ts} +0 -0
- /package/dist/{data-data-table.test.d.ts → components/bulk-edit-dialog.test.d.ts} +0 -0
- /package/dist/components/{table-dimensions.test.d.ts → content-viewer.test.d.ts} +0 -0
- /package/dist/{tableList.test.d.ts → components/form-runner.test.d.ts} +0 -0
- /package/dist/{components/filters/use-debounced-commit.d.ts → hooks/useDebouncedCommit.d.ts} +0 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The single, framework-free result-export utility for the shipped edit-db
|
|
3
|
+
* chain. Every export surface — the data grid, the raw SQL console result grid,
|
|
4
|
+
* and any future report runner — serializes through THIS module so there is
|
|
5
|
+
* exactly one CSV/JSON implementation in the shipped path.
|
|
6
|
+
*
|
|
7
|
+
* The serializers are matrix-based (`string[]` headers + `unknown[][]` rows) so
|
|
8
|
+
* they serve both the grid (record rows projected onto ordered fields) and the
|
|
9
|
+
* console (already-columnar positional rows) without a second code path.
|
|
10
|
+
*
|
|
11
|
+
* Value coercion is deliberate about the type classes that have bitten edit-db
|
|
12
|
+
* before:
|
|
13
|
+
* - NULL vs empty string are distinguishable in CSV (NULL -> empty field,
|
|
14
|
+
* "" -> a quoted empty pair, the PostgreSQL CSV convention).
|
|
15
|
+
* - bigint is rendered as its exact decimal string, never coerced through
|
|
16
|
+
* Number (which silently rounds past 2^53 — a known BigInt-PK bug class).
|
|
17
|
+
* - Date is rendered as a stable ISO-8601 string.
|
|
18
|
+
*/
|
|
19
|
+
export type ExportFormat = 'csv' | 'json';
|
|
20
|
+
export type JsonMode = 'array' | 'lines';
|
|
21
|
+
/** The UTF-8 byte-order mark. Prepending it makes Excel open the CSV as UTF-8. */
|
|
22
|
+
export declare const UTF8_BOM = "\uFEFF";
|
|
23
|
+
/**
|
|
24
|
+
* Render one cell value as an RFC4180 CSV field.
|
|
25
|
+
*
|
|
26
|
+
* `null`/`undefined` become a bare empty field; an empty string becomes a
|
|
27
|
+
* quoted empty pair (`""`) so the two stay distinguishable in the output bytes.
|
|
28
|
+
* bigint and Date get exact/stable string forms; objects are JSON-encoded.
|
|
29
|
+
*/
|
|
30
|
+
export declare function formatCsvCell(value: unknown): string;
|
|
31
|
+
export interface CsvOptions {
|
|
32
|
+
/** Prepend the UTF-8 BOM so Excel detects the encoding. Default false. */
|
|
33
|
+
bom?: boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Serialize a header row plus data rows into an RFC4180 CSV string. Rows are
|
|
37
|
+
* joined with CRLF as the RFC specifies. `rows` are positional, aligned to
|
|
38
|
+
* `headers`.
|
|
39
|
+
*/
|
|
40
|
+
export declare function buildCsv(headers: readonly string[], rows: ReadonlyArray<readonly unknown[]>, options?: CsvOptions): string;
|
|
41
|
+
export interface JsonOptions {
|
|
42
|
+
/** `array` (default): a single pretty-printed JSON array. `lines`: NDJSON. */
|
|
43
|
+
mode?: JsonMode;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Serialize header + rows into JSON, keyed by the header names. `array` mode
|
|
47
|
+
* emits one pretty-printed array; `lines` mode emits newline-delimited JSON
|
|
48
|
+
* (one compact object per line). bigint PKs round-trip losslessly as strings.
|
|
49
|
+
*/
|
|
50
|
+
export declare function buildJson(headers: readonly string[], rows: ReadonlyArray<readonly unknown[]>, options?: JsonOptions): string;
|
|
51
|
+
/** One page of the source result set, as returned by the paging fetcher. */
|
|
52
|
+
export interface ExportPage {
|
|
53
|
+
/** Positional rows, aligned to the export headers. */
|
|
54
|
+
rows: unknown[][];
|
|
55
|
+
/** The server's total row count for the current filter (drives the loop). */
|
|
56
|
+
total: number;
|
|
57
|
+
}
|
|
58
|
+
export interface ExportAllOptions {
|
|
59
|
+
headers: string[];
|
|
60
|
+
format: ExportFormat;
|
|
61
|
+
/**
|
|
62
|
+
* Pull one page at the given offset. This is the ONLY data seam — the caller
|
|
63
|
+
* closes over the existing fetcher/query-builder so the current filters and
|
|
64
|
+
* sort are honored, and no new HTTP client is introduced.
|
|
65
|
+
*/
|
|
66
|
+
fetchPage: (offset: number, limit: number) => Promise<ExportPage>;
|
|
67
|
+
/** Page size for each fetch. Default 500. */
|
|
68
|
+
pageSize?: number;
|
|
69
|
+
/** Hard stop after this many rows; the result is marked `truncated`. */
|
|
70
|
+
rowCap?: number;
|
|
71
|
+
/** Abort mid-export; the promise rejects and no content is assembled. */
|
|
72
|
+
signal?: AbortSignal;
|
|
73
|
+
/** Called after each page with the running fetched count and the total. */
|
|
74
|
+
onProgress?: (fetched: number, total: number) => void;
|
|
75
|
+
csv?: CsvOptions;
|
|
76
|
+
json?: JsonOptions;
|
|
77
|
+
}
|
|
78
|
+
export interface ExportResult {
|
|
79
|
+
content: string;
|
|
80
|
+
rowCount: number;
|
|
81
|
+
total: number;
|
|
82
|
+
truncated: boolean;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Drain the full result set page by page and serialize it once at the end.
|
|
86
|
+
*
|
|
87
|
+
* Cancellation is fail-safe: an aborted signal makes this throw before any
|
|
88
|
+
* content string is built, so the caller — which only writes a file on a
|
|
89
|
+
* resolved result — never produces a partial file. The row cap stops paging
|
|
90
|
+
* early and flags `truncated` so the caller can warn.
|
|
91
|
+
*/
|
|
92
|
+
export declare function exportAllRows(options: ExportAllOptions): Promise<ExportResult>;
|
|
93
|
+
/**
|
|
94
|
+
* Default confirmation threshold: exporting more than this many rows prompts the
|
|
95
|
+
* user first, since it means paging the full result set out of the database.
|
|
96
|
+
*/
|
|
97
|
+
export declare const DEFAULT_ROW_CAP = 100000;
|
|
98
|
+
/** Options passed to a bound {@link ExportRunner}. */
|
|
99
|
+
export interface RunExportOptions {
|
|
100
|
+
format: ExportFormat;
|
|
101
|
+
signal?: AbortSignal;
|
|
102
|
+
onProgress?: (fetched: number, total: number) => void;
|
|
103
|
+
rowCap?: number;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* A surface-bound export runner: closes over the surface's headers + paging
|
|
107
|
+
* fetcher and drains the full result set. Returns null when the surface has
|
|
108
|
+
* nothing to export (no active query/table). This is the seam the grid toolbar
|
|
109
|
+
* and any other paged surface hand to the shared export button.
|
|
110
|
+
*/
|
|
111
|
+
export type ExportRunner = (options: RunExportOptions) => Promise<ExportResult | null>;
|
|
112
|
+
/** MIME type for a downloaded file of the given format. */
|
|
113
|
+
export declare function mimeFor(format: ExportFormat): string;
|
|
114
|
+
/** `<base>-export.<ext>` filename for the given format. */
|
|
115
|
+
export declare function filenameFor(base: string, format: ExportFormat): string;
|
|
116
|
+
/**
|
|
117
|
+
* Trigger a browser download of `content` as `filename`. Builds a Blob, clicks a
|
|
118
|
+
* transient `<a download>`, then revokes the object URL. No-ops outside a DOM.
|
|
119
|
+
*/
|
|
120
|
+
export declare function downloadTextFile(content: string, filename: string, mimeType: string): void;
|
|
121
|
+
/** Download a base64-encoded binary value as a file (image, PDF, any blob). */
|
|
122
|
+
export declare function downloadBinaryFile(base64: string, filename: string, mimeType: string): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { Column } from '../types/schema';
|
|
2
|
+
/**
|
|
3
|
+
* The temporal family of a column, from its DATABASE type (mirrors the server's
|
|
4
|
+
* ValidationRules.TemporalKindOf) — paramType can't carry this, because
|
|
5
|
+
* temporal mutation inputs are String on the wire.
|
|
6
|
+
*/
|
|
7
|
+
export declare function temporalKindOf(column: Column): 'datetime' | 'date' | 'time' | undefined;
|
|
2
8
|
/**
|
|
3
9
|
* Anchors a pattern the same way the HTML5 `pattern` attribute and the server
|
|
4
10
|
* validator do: wrap as ^(?:...)$ unless the author already anchored it with ^.
|
|
@@ -10,3 +16,13 @@ export declare function anchorPattern(pattern: string): string;
|
|
|
10
16
|
* callers keep their own required policy (forms use !isNullable).
|
|
11
17
|
*/
|
|
12
18
|
export declare function validateFieldValue(column: Column, value: unknown, isRequired: boolean): string | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Validates every value PRESENT in a mutation payload against its column's
|
|
21
|
+
* schema rules — the pre-send gate the mutation hooks run so EVERY write path
|
|
22
|
+
* (forms, programmatic callers, future surfaces) is checked, not only fields a
|
|
23
|
+
* form happened to attach validators to. Absent/cleared values are skipped:
|
|
24
|
+
* required-ness is form policy (a NOT NULL column with a DB default is legal to
|
|
25
|
+
* omit on insert), and this gate must never refuse what the server accepts.
|
|
26
|
+
* Returns all failures, or an empty array when the payload is clean.
|
|
27
|
+
*/
|
|
28
|
+
export declare function validateRowValues(columns: readonly Column[], detail: Record<string, unknown>): string[];
|
package/dist/lib/fk.d.ts
CHANGED
|
@@ -15,6 +15,19 @@ export declare function coerceForGql(value: unknown, gqlType: string): unknown;
|
|
|
15
15
|
export declare function gqlTypeOf(column: Column | undefined): string;
|
|
16
16
|
export declare function isFkMember(join: Join, columnName: string): boolean;
|
|
17
17
|
export declare function isComposite(join: Join): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* The destination column paired POSITIONALLY with `columnName` on the source side
|
|
20
|
+
* of `join`, or null when the pairing does not exist.
|
|
21
|
+
*
|
|
22
|
+
* Null is the whole point. The previous inline form was
|
|
23
|
+
* `destinationColumnNames[indexOf(name)] ?? destinationColumnNames[0]`: `indexOf`
|
|
24
|
+
* returns -1 for a column that is not part of the join, the indexed read is
|
|
25
|
+
* `undefined`, and `??` then substituted the FIRST destination column — so an
|
|
26
|
+
* unresolvable pairing silently produced a confident, wrong FK target instead of
|
|
27
|
+
* an absent one. Callers must handle null by declining the FK behaviour, never by
|
|
28
|
+
* guessing a column (see .claude/rules/composite-pk-compliance.md).
|
|
29
|
+
*/
|
|
30
|
+
export declare function fkDestinationColumnFor(join: Join, columnName: string): string | null;
|
|
18
31
|
export declare function findJoinBySource(joins: Join[], columnName: string): Join | undefined;
|
|
19
32
|
/**
|
|
20
33
|
* Build a `{_eq}`-per-column composite filter against the destination side of a join,
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Saved-form definition shape consumed by the form runner.
|
|
3
|
+
*
|
|
4
|
+
* A {@link FormDefinition} is the builder's output (see the desktop shell's
|
|
5
|
+
* `forms/form-state.ts`): a bound single-record data-entry form over one table.
|
|
6
|
+
* The runner binds it to live data. The types are re-declared here — rather than
|
|
7
|
+
* imported from the shell — to keep this shipped stack self-contained (the same
|
|
8
|
+
* choice `common/saved-objects.ts` makes for its `SavedObject` contract), and to
|
|
9
|
+
* give the runner a tolerant parser for the `unknown` `SavedObject.definition`
|
|
10
|
+
* payload it is handed.
|
|
11
|
+
*/
|
|
12
|
+
/** The data-entry widget a field renders as. Mirrors the builder's FormControlType. */
|
|
13
|
+
export type FormControlType = 'text' | 'textarea' | 'number' | 'checkbox' | 'date' | 'datetime' | 'select';
|
|
14
|
+
export interface FormField {
|
|
15
|
+
/** Column name within the bound table. */
|
|
16
|
+
column: string;
|
|
17
|
+
/** Display label shown beside the control. */
|
|
18
|
+
label: string;
|
|
19
|
+
control: FormControlType;
|
|
20
|
+
/** Read-only controls render disabled. */
|
|
21
|
+
readOnly: boolean;
|
|
22
|
+
/** Required marker (runtime enforces via field validation). */
|
|
23
|
+
required: boolean;
|
|
24
|
+
/** Whether the field appears on the form. */
|
|
25
|
+
include: boolean;
|
|
26
|
+
}
|
|
27
|
+
/** A schema-derived related-record section below the parent form. */
|
|
28
|
+
export interface FormSubform {
|
|
29
|
+
/** Stable relationship id, normally the parent collection field. */
|
|
30
|
+
relationship: string;
|
|
31
|
+
childTable: string;
|
|
32
|
+
/** Parent and child FK columns are parallel; composite links include every pair. */
|
|
33
|
+
parentColumns: string[];
|
|
34
|
+
childColumns: string[];
|
|
35
|
+
label: string;
|
|
36
|
+
mode: 'grid' | 'stacked';
|
|
37
|
+
}
|
|
38
|
+
export interface FormDefinition {
|
|
39
|
+
/** Identifier of the bound table (qualified "schema.name" or GraphQL name). */
|
|
40
|
+
table: string;
|
|
41
|
+
/** Form caption. */
|
|
42
|
+
title: string;
|
|
43
|
+
/** Layout columns (1–4): how many fields sit side-by-side. */
|
|
44
|
+
columns: number;
|
|
45
|
+
fields: FormField[];
|
|
46
|
+
subforms?: FormSubform[];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Validates an unknown value (e.g. a `SavedObject.definition`) as a
|
|
50
|
+
* {@link FormDefinition}. Returns null when it cannot be bound — a missing table,
|
|
51
|
+
* or no parseable fields, means there is nothing to render. Unparseable
|
|
52
|
+
* individual fields are dropped (tolerant), mirroring the builder's storage
|
|
53
|
+
* parser, so one bad field never blanks the whole form.
|
|
54
|
+
*/
|
|
55
|
+
export declare function parseFormDefinition(value: unknown): FormDefinition | null;
|
|
56
|
+
/** The included fields, in definition order — what the runner renders. */
|
|
57
|
+
export declare function visibleFields(def: FormDefinition): FormField[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure record-cursor math for the form runner's first/prev/next/last navigation.
|
|
3
|
+
*
|
|
4
|
+
* The runner browses a table one record at a time by absolute offset (a
|
|
5
|
+
* `limit: 1 offset: <index>` fetch), so the only stateful arithmetic is moving a
|
|
6
|
+
* bounded index across `[0, total)`. Keeping it here makes it unit-testable
|
|
7
|
+
* without a fetcher or DOM, and keeps the component thin.
|
|
8
|
+
*/
|
|
9
|
+
export type NavDirection = 'first' | 'prev' | 'next' | 'last';
|
|
10
|
+
/**
|
|
11
|
+
* Resolves the target absolute index for a navigation step, clamped into the
|
|
12
|
+
* valid range. `total <= 0` (empty table) always resolves to 0 so callers show
|
|
13
|
+
* the "no records" state rather than a negative offset.
|
|
14
|
+
*/
|
|
15
|
+
export declare function nextIndex(current: number, total: number, direction: NavDirection): number;
|
|
16
|
+
/** Whether a navigation step from `current` would actually move (false at a bound). */
|
|
17
|
+
export declare function canNavigate(current: number, total: number, direction: NavDirection): boolean;
|
|
18
|
+
/** Human 1-based position label, e.g. "3 of 12". Empty table reads "0 of 0". */
|
|
19
|
+
export declare function positionLabel(index: number, total: number): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Join, Table } from '../types/schema';
|
|
2
|
+
export interface ChildFilterPlan {
|
|
3
|
+
params: string[];
|
|
4
|
+
filter: string;
|
|
5
|
+
variables: Record<string, unknown>;
|
|
6
|
+
}
|
|
7
|
+
/** True only when every parent-key/FK pair can be safely bound. */
|
|
8
|
+
export declare function relationshipIsUsable(parent: Table, child: Table, join: Join): boolean;
|
|
9
|
+
/** Builds a parameterized child filter; composite relationships bind every pair. */
|
|
10
|
+
export declare function childFilterPlan(parent: Table, child: Table, join: Join, row: Record<string, unknown> | null): ChildFilterPlan | null;
|
|
11
|
+
/** Values supplied to a child create form; callers hide these FK controls. */
|
|
12
|
+
export declare function childPrefill(join: Join, row: Record<string, unknown> | null): Record<string, unknown>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { FormControlType, FormField } from './form-definition';
|
|
2
|
+
/** Structural subset of the app-metadata `FieldMetadata` this module reads. */
|
|
3
|
+
export interface FieldWidgetHint {
|
|
4
|
+
/** Widget hint (e.g. `text`, `select`) — overrides the saved control when recognised. */
|
|
5
|
+
widget?: string;
|
|
6
|
+
/** Whether the field is visible. Metadata default is `true`. */
|
|
7
|
+
visible?: boolean;
|
|
8
|
+
/** Whether the field is read-only. Metadata default is `false`. */
|
|
9
|
+
readOnly?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/** The rendered control plus its resolved read-only / visible state. */
|
|
12
|
+
export interface ResolvedWidget {
|
|
13
|
+
control: FormControlType;
|
|
14
|
+
readOnly: boolean;
|
|
15
|
+
visible: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Resolves the effective widget for a field. The metadata widget hint (when it
|
|
19
|
+
* names a known control) wins over the saved control; read-only is the union of
|
|
20
|
+
* both sources (either can lock a field, neither can unlock what the other
|
|
21
|
+
* locked); visibility requires the field be included AND not hidden by metadata.
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveWidget(field: FormField, meta?: FieldWidgetHint): ResolvedWidget;
|
|
24
|
+
/** How a control maps onto a DOM element for rendering. */
|
|
25
|
+
export type ControlRender = {
|
|
26
|
+
kind: 'input';
|
|
27
|
+
type: string;
|
|
28
|
+
} | {
|
|
29
|
+
kind: 'textarea';
|
|
30
|
+
} | {
|
|
31
|
+
kind: 'checkbox';
|
|
32
|
+
} | {
|
|
33
|
+
kind: 'select';
|
|
34
|
+
};
|
|
35
|
+
/** Maps a control type to the element the runner should render. */
|
|
36
|
+
export declare function controlRender(control: FormControlType): ControlRender;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -51,3 +51,29 @@ export declare function formatRelative(date: Date, now?: Date): string;
|
|
|
51
51
|
* `title` revealing the exact value), or the plain string otherwise.
|
|
52
52
|
*/
|
|
53
53
|
export declare function formatColumnValue(value: unknown, column: Column, options?: FormatOptions): ReactNode;
|
|
54
|
+
/**
|
|
55
|
+
* Format a raw date/datetime string for grid-cell display via toLocaleString,
|
|
56
|
+
* blanking sentinel "unset" instants.
|
|
57
|
+
*
|
|
58
|
+
* Only exact placeholder/sentinel instants are blanked, not legitimate
|
|
59
|
+
* historical dates. A broad "before 1973" cutoff hid real dates (e.g. a 1900
|
|
60
|
+
* birthdate or a 1969 record). The two markers that actually mean "unset":
|
|
61
|
+
* the Unix epoch (timestamp 0, i.e. 1970-01-01T00:00:00Z) and .NET/SQL
|
|
62
|
+
* DateTime.MinValue (year 0001-01-01).
|
|
63
|
+
*/
|
|
64
|
+
export declare function formatDateCellValue(value: string): string;
|
|
65
|
+
/**
|
|
66
|
+
* Formats a number into an abbreviated string representation.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```typescript
|
|
70
|
+
* abbreviateNumber(12) // "12"
|
|
71
|
+
* abbreviateNumber(1200) // "1.2k"
|
|
72
|
+
* abbreviateNumber(45000) // "45k"
|
|
73
|
+
* abbreviateNumber(1200000) // "1.2M"
|
|
74
|
+
* ```
|
|
75
|
+
*
|
|
76
|
+
* @param num - Number to abbreviate
|
|
77
|
+
* @returns Abbreviated string representation
|
|
78
|
+
*/
|
|
79
|
+
export declare function abbreviateNumber(num: number | null): string;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { ColumnFiltersState } from '@tanstack/react-table';
|
|
2
|
+
import { Column, Table } from '../types/schema';
|
|
3
|
+
/** URL parameter that owns the selected grouping field. */
|
|
4
|
+
export declare const GRID_GROUP_BY_PARAM = "gb";
|
|
5
|
+
/** Optional, URL-owned numeric measure shown beside every server aggregate. */
|
|
6
|
+
export declare const GRID_GROUP_SUM_PARAM = "gs";
|
|
7
|
+
/**
|
|
8
|
+
* Resolves the URL value against the current schema. Keeping this lookup at
|
|
9
|
+
* the boundary means a stale browser-history value can never become GraphQL
|
|
10
|
+
* source text for a different table.
|
|
11
|
+
*/
|
|
12
|
+
export declare function groupingColumnFromUrl(value: string | null, table: Table | null): Column | null;
|
|
13
|
+
/** The aggregate result is deliberately server-owned; it is never calculated from page rows. */
|
|
14
|
+
export interface GroupingRow {
|
|
15
|
+
value: unknown;
|
|
16
|
+
count: number;
|
|
17
|
+
sum: unknown;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Grouped results have their own aggregate-row ordering. It is intentionally
|
|
21
|
+
* separate from the flat-grid sort: the latter orders individual records,
|
|
22
|
+
* while this controls aggregate buckets. Count ties always use the group key
|
|
23
|
+
* ascending so pagination remains deterministic.
|
|
24
|
+
*/
|
|
25
|
+
export type GroupingSort = {
|
|
26
|
+
field: 'key' | 'count';
|
|
27
|
+
desc: boolean;
|
|
28
|
+
};
|
|
29
|
+
export declare const DEFAULT_GROUPING_SORT: GroupingSort;
|
|
30
|
+
export interface GridGroupingRequest {
|
|
31
|
+
query: string;
|
|
32
|
+
variables: Record<string, unknown>;
|
|
33
|
+
groupBy: Column;
|
|
34
|
+
sumBy: Column | null;
|
|
35
|
+
sort: GroupingSort;
|
|
36
|
+
}
|
|
37
|
+
export interface GridGroupMemberRequest {
|
|
38
|
+
query: string;
|
|
39
|
+
variables: Record<string, unknown>;
|
|
40
|
+
/** Response field selected from the current schema, never parsed from query text. */
|
|
41
|
+
responseKey: string;
|
|
42
|
+
}
|
|
43
|
+
export declare function groupingSumColumnFromUrl(value: string | null, table: Table | null): Column | null;
|
|
44
|
+
/**
|
|
45
|
+
* Update one of the grouping-owned URL fields without disturbing filters, the
|
|
46
|
+
* selected profile, or other application-owned state. The caller receives a
|
|
47
|
+
* fresh instance so it is safe to use during render/effects as well as event
|
|
48
|
+
* handlers.
|
|
49
|
+
*/
|
|
50
|
+
export declare function withGroupingUrlParam(search: URLSearchParams, param: typeof GRID_GROUP_BY_PARAM | typeof GRID_GROUP_SUM_PARAM, value: string | null): URLSearchParams;
|
|
51
|
+
/** Remove grouping state when navigation changes the source table. */
|
|
52
|
+
export declare function withoutGroupingUrlParams(search: URLSearchParams): URLSearchParams;
|
|
53
|
+
/** Builds the schema-derived aggregate document used by the grouped grid. */
|
|
54
|
+
export declare function buildGridGroupingRequest(table: Table, groupBy: Column, columnFilters: ColumnFiltersState, headerFilter: string, sumBy?: Column | null, sort?: GroupingSort): GridGroupingRequest;
|
|
55
|
+
export declare function readGroupingRows(data: Record<string, unknown> | undefined, table: Table, groupBy: Column, sort?: GroupingSort): GroupingRow[];
|
|
56
|
+
/** Defined aggregate ordering for the server-grouped replacement surface. */
|
|
57
|
+
export declare function compareGroupingRowsByKey(left: GroupingRow, right: GroupingRow): number;
|
|
58
|
+
/** Sort aggregate buckets by the selected key/count direction with a stable key tie-breaker. */
|
|
59
|
+
export declare function compareGroupingRows(left: GroupingRow, right: GroupingRow, sort?: GroupingSort): number;
|
|
60
|
+
/** Reads a selected configured measure without ever deriving it from page rows. */
|
|
61
|
+
export declare function readGroupingRowsWithSum(data: Record<string, unknown> | undefined, table: Table, groupBy: Column, sumBy: Column | null, sort?: GroupingSort): GroupingRow[];
|
|
62
|
+
/** Schema-derived member query for a single aggregate key. It merges, never replaces, active filters. */
|
|
63
|
+
export declare function buildGridGroupMemberRequest(table: Table, groupBy: Column, value: unknown, columnFilters: ColumnFiltersState, headerFilter: string, sort?: readonly string[]): GridGroupMemberRequest;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/lib/m2m.d.ts
CHANGED
|
@@ -31,11 +31,36 @@ export declare function m2mRowsQuery(junction: Table, target: Table, m2m: ManyTo
|
|
|
31
31
|
};
|
|
32
32
|
export interface M2mTargetPickerPlan {
|
|
33
33
|
query: string;
|
|
34
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Columns that together identify a target row — every PK column in declaration
|
|
36
|
+
* order. Both the picker and the already-linked set must key on ALL of them
|
|
37
|
+
* (via {@link m2mTargetIdentityColumns}) or two rows sharing their first key
|
|
38
|
+
* column collapse into one.
|
|
39
|
+
*/
|
|
40
|
+
identityColumns: string[];
|
|
41
|
+
/**
|
|
42
|
+
* The target column the junction FK references — the value written on attach.
|
|
43
|
+
* Deliberately separate from identity: on a composite-PK target, or a junction
|
|
44
|
+
* that points at a unique non-PK column, the two are different columns.
|
|
45
|
+
*/
|
|
46
|
+
linkColumn: string;
|
|
35
47
|
/** True when the label column is String-typed and thus supports the
|
|
36
48
|
* server-side `_contains` search; callers fall back to client filtering. */
|
|
37
49
|
serverSearch: boolean;
|
|
38
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* The columns that identify a target row, in declaration order.
|
|
53
|
+
*
|
|
54
|
+
* Single source of truth for BOTH sides of the picker's duplicate check — the
|
|
55
|
+
* fetched target rows and the already-linked rows read off the junction. Keying
|
|
56
|
+
* the two independently (or on the first PK column alone) let two distinct composite-PK
|
|
57
|
+
* rows that share their first key column collapse: one was wrongly marked
|
|
58
|
+
* "Already linked" and its button disabled, making a real target unpickable.
|
|
59
|
+
*
|
|
60
|
+
* Falls back to the junction's target columns when the target declares no primary
|
|
61
|
+
* key, which is the only identity available in that case.
|
|
62
|
+
*/
|
|
63
|
+
export declare function m2mTargetIdentityColumns(target: Table, m2m: ManyToManyJoin): string[];
|
|
39
64
|
/**
|
|
40
65
|
* Build the target-list query used when adding a many-to-many link.
|
|
41
66
|
* Sort values are BifrostQL enum literals, so they must not be quoted.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Column, Table } from '../types/schema';
|
|
2
|
+
import { PkFilter } from './row-id';
|
|
3
|
+
export declare function coerceNumericValue(value: unknown, paramType: string, columnName: string): number;
|
|
4
|
+
export interface ColumnJoin {
|
|
5
|
+
column: Column;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Coerces a form/grid detail object into a wire-ready mutation payload: numeric
|
|
9
|
+
* columns become numbers, BigInt/Decimal stay strings (precision beyond 2^53),
|
|
10
|
+
* booleans and JSON columns get their scalar shapes, empty values follow the
|
|
11
|
+
* insert-vs-update semantics (omit so DB defaults apply vs null to clear), and —
|
|
12
|
+
* for updates — the primary-key columns are overwritten from the resolved
|
|
13
|
+
* <paramref>pkFilter</paramref> so a payload can never retarget another row.
|
|
14
|
+
* Shared by the single-row hook and the bulk delta paths.
|
|
15
|
+
*/
|
|
16
|
+
export declare function coerceDetail(detail: Record<string, unknown>, editColumns: ColumnJoin[], idColumns: Column[], pkFilter: PkFilter | null, isInsert: boolean): Record<string, unknown>;
|
|
17
|
+
/**
|
|
18
|
+
* Builds the `updated:` payload list for a bulk edit: for each FRESH row
|
|
19
|
+
* snapshot, echo every write-set column from the snapshot (the server's
|
|
20
|
+
* `Update_<t>` input requires all non-nullable columns), overlay the shared
|
|
21
|
+
* change set, validate, and coerce — with the primary key taken from the
|
|
22
|
+
* snapshot itself, composite-safe, so a payload can never retarget another row.
|
|
23
|
+
* Throws (never silently skips) when a row's key cannot be resolved or a merged
|
|
24
|
+
* payload fails validation: a bulk edit either stages every selected row or none.
|
|
25
|
+
*/
|
|
26
|
+
export declare function buildBulkUpdatePayloads(table: Pick<Table, 'primaryKeys'>, writeColumns: Column[], idColumns: Column[], freshRows: readonly Record<string, unknown>[], changes: Record<string, unknown>): Record<string, unknown>[];
|
|
27
|
+
/**
|
|
28
|
+
* Builds the `updated:` payload list for STAGED inline cell edits: unlike the
|
|
29
|
+
* shared-change bulk edit, each row carries its OWN change map. Per fresh row:
|
|
30
|
+
* the write set is every non-nullable editable column plus that row's changed
|
|
31
|
+
* columns (the server's `Update_<t>` input requires the non-nullable echo), the
|
|
32
|
+
* snapshot fills the echo, the row's changes overlay it, and the primary key
|
|
33
|
+
* comes from the snapshot itself. Throws — never skips — when a fetched row has
|
|
34
|
+
* no staged changes or a merged payload fails validation: Save all either
|
|
35
|
+
* stages every edited row or none.
|
|
36
|
+
*/
|
|
37
|
+
export declare function buildStagedUpdatePayloads(table: Pick<Table, 'primaryKeys'>, editableColumns: Column[], idColumns: Column[], freshRows: readonly Record<string, unknown>[], changesForRow: (row: Record<string, unknown>) => Record<string, unknown> | undefined): Record<string, unknown>[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|