@inandu-solutions/grid-vue 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +196 -3
  3. package/dist/components/DataRow.vue.d.ts +34 -0
  4. package/dist/components/EditCell.vue.d.ts +16 -0
  5. package/dist/components/FilterControl.vue.d.ts +16 -0
  6. package/dist/components/InanduGrid.vue.d.ts +231 -0
  7. package/dist/components/internal.d.ts +60 -0
  8. package/dist/components/types.d.ts +6 -0
  9. package/dist/composables/useInanduGrid.d.ts +260 -0
  10. package/dist/composables/useInanduGridServer.d.ts +53 -0
  11. package/dist/core/aggregate.d.ts +5 -0
  12. package/dist/core/columns.d.ts +11 -0
  13. package/dist/core/constants.d.ts +14 -0
  14. package/dist/core/dom.d.ts +8 -0
  15. package/dist/core/export/csv.d.ts +2 -0
  16. package/dist/core/export/download.d.ts +2 -0
  17. package/dist/core/export/markup.d.ts +2 -0
  18. package/dist/core/export/pdf.d.ts +3 -0
  19. package/dist/core/filter.d.ts +26 -0
  20. package/dist/core/format.d.ts +24 -0
  21. package/dist/core/i18n/en.d.ts +46 -0
  22. package/dist/core/i18n/es.d.ts +46 -0
  23. package/dist/core/i18n/fr.d.ts +46 -0
  24. package/dist/core/i18n/index.d.ts +8 -0
  25. package/dist/core/i18n/it.d.ts +46 -0
  26. package/dist/core/i18n/zh.d.ts +46 -0
  27. package/dist/core/index.d.ts +17 -0
  28. package/dist/core/parse.d.ts +14 -0
  29. package/dist/core/sort.d.ts +1 -0
  30. package/dist/core/tree.d.ts +28 -0
  31. package/dist/core/types.d.ts +46 -0
  32. package/dist/inandu-grid-vue.cjs +3 -0
  33. package/dist/inandu-grid-vue.js +1973 -0
  34. package/dist/index.d.ts +13 -0
  35. package/dist/style.css +358 -0
  36. package/dist/utils/exporters.d.ts +29 -0
  37. package/dist/utils/measureColumn.d.ts +15 -0
  38. package/dist/utils/translate.d.ts +10 -0
  39. package/package.json +92 -6
@@ -0,0 +1,260 @@
1
+ import { MaybeRefOrGetter, VNodeChild } from 'vue';
2
+ import { InanduColumnAggregate, InanduColumnStickySide, InanduColumnType, InanduGridColumnFilterValue, InanduGridRow, TreeVisibleRow } from '../core';
3
+ /**
4
+ * Plain-value column definition for the Vue API — friendlier than the core's `ColumnConfig`
5
+ * getter shape (`field()`, `type()`, ...), which exists to match grid-angular's signal-based
6
+ * columns. `toColumnConfig` below adapts one to the other so the ported core needs no changes.
7
+ */
8
+ export interface InanduGridColumn {
9
+ field: string;
10
+ headerText?: string;
11
+ type?: InanduColumnType;
12
+ format?: string;
13
+ order?: number;
14
+ aggregate?: InanduColumnAggregate;
15
+ /** Whether this column can be picked in the "group by" control. Default: true. */
16
+ groupable?: boolean;
17
+ /** Whether this field shows an editable control while its row is in edit/add mode. Default: false. */
18
+ editable?: boolean;
19
+ required?: boolean;
20
+ /** `'number'` columns only. */
21
+ min?: number;
22
+ /** `'number'` columns only. */
23
+ max?: number;
24
+ /** Checked against the raw (unparsed) draft value, `'string'`/`'number'`/`'date'` columns only. */
25
+ pattern?: string;
26
+ /** Runs after required/min/max/pattern all pass, against the parsed value and the row's other parsed fields (for cross-field checks). */
27
+ validator?: (parsed: unknown, parsedRow: Record<string, unknown>) => string | null;
28
+ /** Runs only once every synchronous rule (above) has already passed for this field; every field's async check runs concurrently. */
29
+ asyncValidator?: (parsed: unknown, parsedRow: Record<string, unknown>) => Promise<string | null>;
30
+ /** Whether this column can be hidden via the column-visibility toggle. Default: true. */
31
+ hideable?: boolean;
32
+ /** Pins the column to that side of the table, `position: sticky`, regardless of scroll. Unset: not pinned. */
33
+ pinned?: InanduColumnStickySide;
34
+ /** Fixed column width in px — also what `stickyOffset`/`stickyOffsetRight` stack against. Default (unset): 80, same fallback grid-angular's `effectiveWidth() || 80` uses. */
35
+ width?: number;
36
+ /** Whether this column's header can be dragged to reorder columns. Default: true. */
37
+ reorder?: boolean;
38
+ /**
39
+ * Custom rendering for this column's cells, in place of the built-in formatted text — a render
40
+ * function returning VNodes (use `h()`). The `#cell-<field>` scoped slot on `<InanduGrid>` does the
41
+ * same thing template-side and wins when both are set. Doesn't affect editing — pair with
42
+ * `renderEditor` (or `#editor-<field>`) if this column is also `editable`.
43
+ */
44
+ renderCell?: (ctx: InanduCellRenderContext) => VNodeChild;
45
+ /** Custom rendering for this column's header label (or the `#header-<field>` slot). The sort arrow, multi-sort priority badge, and resize handle next to it are unaffected — same scope as grid-angular's `#inanduHeaderTemplate`. */
46
+ renderHeader?: (ctx: InanduHeaderRenderContext) => VNodeChild;
47
+ /** Custom editor for this column's cells while their row is in edit/create mode (or the `#editor-<field>` slot), in place of the built-in type-aware `<input>`/`<select>`. The column must still be `editable`; save-time parsing/validation is unaffected — call `setValue` with the same raw control shape the built-in control would produce (a string for `'string'`/`'number'`/`'date'`, a boolean for `'boolean'`). */
48
+ renderEditor?: (ctx: InanduEditRenderContext) => VNodeChild;
49
+ }
50
+ /** Passed to a column's `renderCell` / `#cell-<field>` slot — the same information the built-in formatted-text cell would use. */
51
+ export interface InanduCellRenderContext {
52
+ value: unknown;
53
+ row: InanduGridRow;
54
+ field: string;
55
+ }
56
+ /** Passed to a column's `renderHeader` / `#header-<field>` slot. `title` is the resolved display label (`headerText` if set, else `field` — the same fallback the built-in plain-text header uses). */
57
+ export interface InanduHeaderRenderContext {
58
+ title: string;
59
+ field: string;
60
+ }
61
+ /** Passed to a column's `renderEditor` / `#editor-<field>` slot. `row` is `{}` for the add-new-row draft. */
62
+ export interface InanduEditRenderContext {
63
+ value: unknown;
64
+ row: InanduGridRow;
65
+ field: string;
66
+ setValue: (value: unknown) => void;
67
+ error: string | undefined;
68
+ }
69
+ /** Passed to `onRowSave` — everything `saveRow()` parsed and validated for one already-existing row. */
70
+ export interface InanduGridRowSave {
71
+ row: InanduGridRow;
72
+ values: Record<string, unknown>;
73
+ }
74
+ /** One cell update from a `pasteAt()` call — one entry per pasted cell that landed on an existing row and an editable column. */
75
+ export interface InanduGridCellPaste {
76
+ row: InanduGridRow;
77
+ field: string;
78
+ value: unknown;
79
+ }
80
+ /**
81
+ * What `onQueryChange` emits in `serverSide` mode: the grid's whole request state, ready for
82
+ * `useInanduGridServer` (or a hand-rolled fetch) to turn into a query string. `page` is 0-based,
83
+ * like the composable's own `page`.
84
+ */
85
+ export interface InanduGridServerQuery {
86
+ page: number;
87
+ pageSize: number;
88
+ sortCriteria: InanduGridSort[];
89
+ filterValues: Record<string, InanduGridColumnFilterValue>;
90
+ filterQuery: string;
91
+ }
92
+ export interface InanduGridSort {
93
+ field: string;
94
+ direction: 'asc' | 'desc';
95
+ }
96
+ /** One bucket of `useInanduGrid`'s `groups` — mirrors grid-angular's `groupedRows`. */
97
+ export interface InanduGridGroup {
98
+ /** The grouped column's *formatted* display value for every row in this bucket. */
99
+ key: string;
100
+ rows: InanduGridRow[];
101
+ /** One entry per column with `aggregate` set, keyed by that column's field. */
102
+ aggregates: Record<string, number>;
103
+ }
104
+ export interface UseInanduGridOptions {
105
+ rows: InanduGridRow[];
106
+ columns: InanduGridColumn[];
107
+ locale?: string;
108
+ /** Drives validation-message translation. Default: `locale`'s primary subtag. */
109
+ lang?: string;
110
+ /** 0 disables pagination — `visibleRows` is the full sorted/filtered set. Default: 0. */
111
+ pageSize?: number;
112
+ /** Whether the caller renders a select-checkbox column — `stickyOffset()` needs to know, since that column (when present) is always sticky-left too. Default: false. */
113
+ selectable?: boolean;
114
+ /** `window.confirm()`-gates `deleteRow()`, same as grid-angular. Unset: deletes immediately. */
115
+ deleteConfirmMessage?: string;
116
+ /** `window.confirm()`-gates `deleteSelectedRows()`. Unset: deletes immediately. */
117
+ bulkDeleteConfirmMessage?: string;
118
+ /** Called by `saveRow()` once validation passes — the grid never mutates `rows` itself; applying the change is the caller's job. */
119
+ onRowSave?: (event: InanduGridRowSave) => void;
120
+ /** Called by `saveNewRow()` once validation passes. No row reference — it doesn't exist in `rows` yet. */
121
+ onRowCreate?: (values: Record<string, unknown>) => void;
122
+ /** Called by `deleteRow()` (after confirmation, if any). */
123
+ onRowDelete?: (row: InanduGridRow) => void;
124
+ /** Called by `deleteSelectedRows()` (after confirmation, if any) — every currently-selected row. */
125
+ onRowsDelete?: (rows: InanduGridRow[]) => void;
126
+ /** Called by `pasteAt()` with every parsed cell update — the grid never mutates `rows` itself. */
127
+ onCellsPaste?: (updates: InanduGridCellPaste[]) => void;
128
+ /** Called by `onRowDrop()` with the fully reordered row array — the grid never mutates `rows` itself. */
129
+ onRowOrderChange?: (rows: InanduGridRow[]) => void;
130
+ /**
131
+ * Turns on tree mode: the name of the field on each row holding its child rows (a nested array
132
+ * of the same shape). `rows` is then the root rows; free-text/column filters keep a node when it
133
+ * or any descendant matches, and the active sort orders each level of siblings. Auto-disabled
134
+ * while grouped, same restriction grid-angular's `treeChildrenKey` has (there it also excludes
135
+ * virtual-scroll/server-side, neither of which this port has). Unset (default): tree mode off.
136
+ */
137
+ treeChildrenKey?: string;
138
+ /** Initial expand state for tree mode: `'none'` (default), `'all'`, or a max depth to open to. */
139
+ treeDefaultExpanded?: 'none' | 'all' | number;
140
+ /** Whether master-detail is on — the caller checks this itself, this only controls `toggleRowExpanded`'s accordion behavior. Off (any number of rows can be expanded at once) by default. */
141
+ singleDetailExpand?: boolean;
142
+ /**
143
+ * Opt-in row virtualization for large datasets — bypasses pagination entirely (same as grouping),
144
+ * feeding the windowing logic the full sorted/filtered set instead. Auto-disables grouping,
145
+ * master-detail, tree data, row drag-reorder and clipboard, same restrictions grid-angular's
146
+ * `virtualScroll` has (there it also covers server-side infinite-scroll, which this port doesn't
147
+ * have). The actual DOM windowing (which rows are mounted, given a real scroll position) is the
148
+ * `<InanduGrid>` component's job, not this composable's — it has no way to observe scroll state itself.
149
+ */
150
+ virtualScroll?: boolean;
151
+ /** An extra per-row predicate ANDed onto the free-text + column filters — same as grid-angular's `extraRowFilter`. Lets a caller layer its own filtering (e.g. an advanced-filter query) on top of the grid's own. Unset: no extra filtering. */
152
+ extraRowFilter?: (row: InanduGridRow) => boolean;
153
+ /**
154
+ * Server-side mode: `rows` is already the current page, as sorted/filtered/paged by the server
155
+ * (see `useInanduGridServer`). The composable keeps owning the sort / filter / search / page
156
+ * *state* so `<InanduGrid>`'s controls work unchanged, but stops applying it to `rows` and reads
157
+ * the page count from `totalItems`. Grouping, tree data, virtual scroll, `extraRowFilter` and
158
+ * column-aggregate totals are not available (they would only see one page). Unset: client-side.
159
+ */
160
+ serverSide?: {
161
+ totalItems: number;
162
+ };
163
+ /** `serverSide` only: called once on setup and again whenever page, sort, filters or search change. */
164
+ onQueryChange?: (query: InanduGridServerQuery) => void;
165
+ }
166
+ /**
167
+ * Headless engine for the grid: owns sort + per-column filter + pagination state and derives
168
+ * `visibleRows` (the current page) and `filteredRowCount` (before pagination) from `rows` using
169
+ * the same pure core as grid-angular. No rendering — `<InanduGrid>` is one consumer of this
170
+ * composable, not the only way to use it.
171
+ *
172
+ * `options` may be a plain object, a ref, or a getter — pass a getter (`() => ({ rows: rows.value,
173
+ * columns })`) to have the grid follow reactive data. The returned object exposes every piece of
174
+ * state as a reactive *getter* (read `grid.visibleRows`, not `grid.visibleRows.value`), so reading
175
+ * it inside a template, `computed` or `watch` tracks it. Don't destructure state properties out of
176
+ * it — that snapshots their current value; the methods are safe to destructure.
177
+ */
178
+ export declare function useInanduGrid(options: MaybeRefOrGetter<UseInanduGridOptions>): {
179
+ readonly visibleColumns: InanduGridColumn[];
180
+ /** Every column in current drag-reorder order, hidden ones included — same as grid-angular's `displayColumns()`. Most rendering should use `visibleColumns` instead; this is for callers (like a saved-view restore) that need to address a currently-hidden column too. */
181
+ readonly allColumns: InanduGridColumn[];
182
+ stickyOffset: (field: string) => number;
183
+ stickyOffsetRight: (field: string) => number;
184
+ columnPinnedSide: (field: string) => InanduColumnStickySide | undefined;
185
+ setColumnPinned: (field: string, side: InanduColumnStickySide | undefined) => void;
186
+ readonly hideableColumns: InanduGridColumn[];
187
+ isColumnHidden: (field: string) => boolean;
188
+ toggleColumnVisibility: (field: string) => void;
189
+ setColumnOrder: (fields: readonly string[]) => void;
190
+ readonly draggingField: string | undefined;
191
+ onColumnDragStart: (field: string) => void;
192
+ onColumnDrop: (targetField: string) => void;
193
+ effectiveWidth: (field: string) => number;
194
+ isColumnResized: (field: string) => boolean;
195
+ setColumnWidth: (field: string, width: number) => void;
196
+ readonly draggingRow: InanduGridRow | undefined;
197
+ onRowDragStart: (row: InanduGridRow) => void;
198
+ onRowDrop: (targetRow: InanduGridRow) => void;
199
+ readonly hasTreeData: boolean;
200
+ readonly treeRows: TreeVisibleRow<InanduGridRow>[];
201
+ isTreeRowExpanded: (row: InanduGridRow) => boolean;
202
+ toggleTreeRow: (row: InanduGridRow) => void;
203
+ setAllTreeRowsExpanded: (expanded: boolean) => void;
204
+ isRowExpanded: (row: InanduGridRow) => boolean;
205
+ toggleRowExpanded: (row: InanduGridRow) => void;
206
+ readonly hasVirtualScroll: boolean;
207
+ readonly visibleRows: InanduGridRow[];
208
+ /** Same "what's on screen right now" set the select-all checkbox and CSV/Excel/PDF export use — the current page, or every group's rows while grouped. */
209
+ readonly exportRows: InanduGridRow[];
210
+ /** Every row matching the current filter and sort, in sort order, regardless of pagination or grouping — same as grid-angular's `sortedData()`. Unlike `exportRows`, this never stops at one page: a full-dataset export/print reads from here instead. */
211
+ readonly sortedRows: InanduGridRow[];
212
+ readonly filteredRowCount: number;
213
+ readonly locale: string;
214
+ readonly sortCriteria: InanduGridSort[];
215
+ setSort: (criteria: InanduGridSort[]) => void;
216
+ toggleSort: (field: string, additive?: boolean) => void;
217
+ sortDirectionFor: (field: string) => "asc" | "desc" | undefined;
218
+ sortPriorityFor: (field: string) => number | undefined;
219
+ readonly filterValues: Record<string, InanduGridColumnFilterValue>;
220
+ setFilterValue: (field: string, value: InanduGridColumnFilterValue) => void;
221
+ clearAllFilters: () => void;
222
+ readonly filterQuery: string;
223
+ setFilterQuery: (query: string) => void;
224
+ readonly page: number;
225
+ setPage: (next: number) => void;
226
+ readonly pageCount: number;
227
+ readonly groupByField: string | undefined;
228
+ setGroupByField: (field: string | undefined) => void;
229
+ readonly groups: InanduGridGroup[] | null;
230
+ readonly totals: Record<string, number>;
231
+ readonly selectedRows: Set<InanduGridRow>;
232
+ isRowSelected: (row: InanduGridRow) => boolean;
233
+ toggleRowSelection: (row: InanduGridRow) => void;
234
+ readonly allSelected: boolean;
235
+ readonly someSelected: boolean;
236
+ toggleSelectAll: () => void;
237
+ clearSelection: () => void;
238
+ readonly editableColumns: InanduGridColumn[];
239
+ readonly editingRow: InanduGridRow | undefined;
240
+ readonly isAddingRow: boolean;
241
+ readonly rowDraft: Record<string, unknown>;
242
+ readonly fieldErrors: Record<string, string>;
243
+ readonly isValidating: boolean;
244
+ isEditingRow: (row: InanduGridRow) => boolean;
245
+ isAnotherRowEditing: (row: InanduGridRow) => boolean;
246
+ startEditingRow: (row: InanduGridRow) => void;
247
+ cancelRowEdit: () => void;
248
+ setRowDraftValue: (field: string, value: unknown) => void;
249
+ saveRow: (row: InanduGridRow) => Promise<void>;
250
+ deleteRow: (row: InanduGridRow) => void;
251
+ deleteSelectedRows: () => void;
252
+ startAddingRow: () => void;
253
+ cancelAddRow: () => void;
254
+ saveNewRow: () => Promise<void>;
255
+ validateCell: (column: InanduGridColumn, value: unknown, row?: Record<string, unknown>) => string | null;
256
+ copyCellText: (rowIndex: number, field: string) => string;
257
+ pasteAt: (anchorRowIndex: number, anchorField: string, text: string) => void;
258
+ };
259
+ /** What `useInanduGrid()` returns — also what `<InanduGrid>` exposes as its `grid` template-ref property. */
260
+ export type InanduGridApi = ReturnType<typeof useInanduGrid>;
@@ -0,0 +1,53 @@
1
+ import { ShallowRef } from 'vue';
2
+ import { InanduGridRow } from '../core';
3
+ import { InanduGridServerQuery } from './useInanduGrid';
4
+ export interface InanduGridQueryOptions {
5
+ /** Extra params appended after the grid's own (a tenant id, a fixed filter, ...). */
6
+ extraParams?: Record<string, string>;
7
+ }
8
+ /**
9
+ * Turns the grid's request state into the query string `Inandu.Grid.Extensions` (the .NET package)
10
+ * parses — the contract is documented at
11
+ * https://github.com/inandusolutions/inandu-grid-extensions/blob/main/docs/request-contract.md.
12
+ *
13
+ * Param order is deterministic (page, pageSize, sort, q, then column filters in field order) so the
14
+ * result can be compared as a plain string.
15
+ */
16
+ export declare function buildInanduGridQuery(query: InanduGridServerQuery, options?: InanduGridQueryOptions): URLSearchParams;
17
+ export interface InanduGridServerOptions extends InanduGridQueryOptions {
18
+ /** The endpoint, e.g. `/api/products`. The grid's params are appended as a query string. */
19
+ url: string;
20
+ /** Passed to `fetch` (auth headers, `credentials`, ...). */
21
+ init?: RequestInit;
22
+ /** Replaces `fetch` — handy for tests or a typed API client. Must honour `signal`. */
23
+ fetcher?: (url: string, init: RequestInit) => Promise<Response>;
24
+ /** Wait this long after the last change before requesting (typing in the search box). The first request is never delayed. Default: 200. */
25
+ debounceMs?: number;
26
+ }
27
+ export interface InanduGridServerResult {
28
+ /** The current page. The previous page stays here while the next one loads, so the table doesn't flash empty. */
29
+ rows: ShallowRef<InanduGridRow[]>;
30
+ /** Total rows matching the filters, across all pages. Also kept during a reload. */
31
+ total: ShallowRef<number>;
32
+ loading: ShallowRef<boolean>;
33
+ error: ShallowRef<Error | undefined>;
34
+ /** Wire this to `<InanduGrid @query-change>` / `useInanduGrid`'s `onQueryChange`. */
35
+ setQuery: (query: InanduGridServerQuery) => void;
36
+ /** Re-issues the last request (after an edit, say). */
37
+ reload: () => void;
38
+ }
39
+ /**
40
+ * Data layer for `serverSide` mode. Pair it with the grid:
41
+ *
42
+ * ```vue
43
+ * <script setup>
44
+ * const server = useInanduGridServer({ url: '/api/products' });
45
+ * </script>
46
+ * <InanduGrid :rows="server.rows.value" :columns="columns" :page-size="25"
47
+ * :server-side="{ totalItems: server.total.value }" @query-change="server.setQuery" />
48
+ * ```
49
+ *
50
+ * Expects the `Inandu.Grid.Extensions` response shape, `{ data: [...], total: n }`. A request that
51
+ * is superseded by a newer one is aborted, and a late response from it is ignored.
52
+ */
53
+ export declare function useInanduGridServer(options: InanduGridServerOptions): InanduGridServerResult;
@@ -0,0 +1,5 @@
1
+ import { ColumnConfig, InanduColumnAggregate, InanduGridRow } from './types';
2
+ /** Short, language-agnostic symbol shown for each `InanduColumnAggregate` kind — deliberately not translated (the same way units like "CSV"/"PDF" aren't). */
3
+ export declare const AGGREGATE_SYMBOLS: Record<Exclude<InanduColumnAggregate, ''>, string>;
4
+ /** One numeric aggregate per `aggregateColumns` entry, computed over `rows` — `'count'` just counts rows (works for any column type); the rest ignore rows whose raw value isn't a finite number. */
5
+ export declare function computeGroupAggregates(rows: InanduGridRow[], aggregateColumns: readonly ColumnConfig[]): Record<string, number>;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Columns with an explicit `order` land at that 0-based slot; columns without one
3
+ * fill the remaining slots in declaration order. Equal/out-of-range `order` values
4
+ * degrade gracefully by falling back to declaration order among themselves.
5
+ *
6
+ * Generic over the column type: works for `InanduColumnComponent` instances or any object with
7
+ * an `order()` accessor.
8
+ */
9
+ export declare function placeColumnsByOrder<C extends {
10
+ order(): number | undefined;
11
+ }>(columns: readonly C[]): C[];
@@ -0,0 +1,14 @@
1
+ /** Floor enforced while dragging a resize handle, so a column can never be shrunk to zero/negative width. */
2
+ export declare const MIN_COLUMN_WIDTH = 30;
3
+ /** Ceiling `autosize` (double-click a resize handle to fit content) will grow a column to, so one
4
+ * very long value can't blow the column out to the width of the page. The drag handle itself is
5
+ * not capped by this. */
6
+ export declare const MAX_COLUMN_WIDTH = 600;
7
+ /** Must match `.inandu-select-column`'s CSS width — used by `stickyOffset()` to account for the (always-sticky) checkbox column. */
8
+ export declare const SELECT_COLUMN_WIDTH = 36;
9
+ /** Must match `.inandu-row-drag-column`'s CSS width — used by `stickyOffset()` to account for the (always-leading, when enabled) drag-handle column. */
10
+ export declare const ROW_DRAG_COLUMN_WIDTH = 32;
11
+ /** Must match `.inandu-detail-toggle-column`'s CSS width — used by `stickyOffset()` to account for the (always-leading-most, when enabled) master-detail expand/collapse column. */
12
+ export declare const DETAIL_TOGGLE_COLUMN_WIDTH = 32;
13
+ /** `localStorage` key prefix for `stateKey`-based persistence, so this library's own saved blobs don't collide with anything else a host app stores. */
14
+ export declare const STATE_STORAGE_PREFIX = "inandu-grid-state:";
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Escapes `value` for use inside a double-quoted CSS attribute-selector value (e.g.
3
+ * `` `td[data-field="${escaped}"]` ``), for the rare runtime with no native `CSS.escape`. Backslashes
4
+ * MUST be escaped before quotes — escaping only quotes lets a value containing a backslash (e.g.
5
+ * `a\"b`) smuggle an unescaped quote through (`\"` in the output reads back as an escaped backslash
6
+ * followed by a bare, string-terminating `"`), letting the selector break out of the attribute value.
7
+ */
8
+ export declare function escapeAttributeSelectorValue(value: string): string;
@@ -0,0 +1,2 @@
1
+ /** Quotes `value` for a CSV cell (doubling internal quotes) only if it contains a comma, quote, or newline. */
2
+ export declare function escapeCsvValue(value: string): string;
@@ -0,0 +1,2 @@
1
+ /** Triggers a browser download of `content` as `filename`, via a throwaway object URL + `<a>` click. */
2
+ export declare function downloadBlob(content: string | Blob, filename: string, mimeType?: string): void;
@@ -0,0 +1,2 @@
1
+ /** Escapes `&`/`</>` for safe use as XML/HTML element text content — shared by `exportExcel()` and `printTable()`. */
2
+ export declare function escapeMarkup(value: string): string;
@@ -0,0 +1,3 @@
1
+ import { jsPDF } from 'jspdf';
2
+ /** Shortens `text` with a trailing "…" until it fits `maxWidth` (in the PDF's current font), unchanged if it already fits. */
3
+ export declare function truncatePdfText(doc: jsPDF, text: string, maxWidth: number): string;
@@ -0,0 +1,26 @@
1
+ import { ColumnConfig, InanduGridColumnFilterValue, InanduGridRow } from './types';
2
+ import { NumberFormatter } from './format';
3
+ /**
4
+ * Whether `value` has at least one non-empty key — an entry can exist (e.g. after typing then
5
+ * clearing) without actually narrowing anything. `values` is the one exception: unlike every other
6
+ * key, an *empty* array is still meaningful (see `matchesColumnFilter`) — the presence of the key
7
+ * at all is what counts, not its length. Only an entirely absent/`undefined` `values` reads as "no
8
+ * set-filter constraint".
9
+ */
10
+ export declare function hasMeaningfulFilterValue(value: InanduGridColumnFilterValue | undefined): boolean;
11
+ /**
12
+ * Whether `row` satisfies `column`'s own filter control. An empty/absent `filterValue` (no
13
+ * constraint entered for this column yet) always matches. Range checks (`'number'`/`'date'`) are
14
+ * inclusive on both ends; a cell that can't be parsed as that column's type never matches once at
15
+ * least one bound is set.
16
+ *
17
+ * `filterValue.values` (the set filter) is the one key that's *present-vs-absent*, not
18
+ * *non-empty-vs-empty*: an `undefined` `values` falls through to the type-specific checks below,
19
+ * same as omitting it; a *defined* `values` — even `[]` — takes over matching for the column
20
+ * entirely, checked against the formatted display value. This is deliberate: `[]` is how "every
21
+ * checkbox is unchecked" (match nothing) is told apart from "the set filter was never touched"
22
+ * (match everything, i.e. don't constrain) — collapsing them to the same "no constraint" meaning,
23
+ * like every other filter key's blank state does, would make unchecking every box a no-op instead
24
+ * of the empty result a consumer building a set-filter checklist actually needs.
25
+ */
26
+ export declare function matchesColumnFilter(column: ColumnConfig, row: InanduGridRow, filterValue: InanduGridColumnFilterValue, locale: string, numberFormatter?: NumberFormatter): boolean;
@@ -0,0 +1,24 @@
1
+ import { InanduColumnType } from './types';
2
+ /**
3
+ * Turns a raw numeric value into its display string. `digitsInfo` is a `DecimalPipe`-style
4
+ * `'{minInt}.{minFrac}-{maxFrac}'` string (e.g. `"1.2-2"`), or empty for the default.
5
+ *
6
+ * Injected into `formatCellValue()` so the framework layer can choose the implementation: the
7
+ * Angular component passes one backed by `@angular/common`'s `formatNumber` (honouring whatever
8
+ * locale data the host app registered), keeping its output byte-identical to before this folder
9
+ * existed. `defaultNumberFormatter` below is the standalone fallback for a non-Angular consumer.
10
+ */
11
+ export type NumberFormatter = (value: number, locale: string, digitsInfo?: string) => string;
12
+ /** `Intl.NumberFormat`-based `NumberFormatter`. Parses the `digitsInfo` mini-syntax; falls back to Angular's own default of up to 3 fraction digits (`'1.0-3'`) when it's absent. */
13
+ export declare function defaultNumberFormatter(value: number, locale: string, digitsInfo?: string): string;
14
+ /**
15
+ * Coerces a raw cell value to a `Date` for `type="date"` columns — accepts a `Date`, a timestamp,
16
+ * or a string. Safari's `Date` string parser is stricter than Chrome's; notably it returns an
17
+ * Invalid Date for a space-separated `"yyyy-mm-dd hh:mm[:ss]"` that Chrome happily parses. That one
18
+ * common case is normalised to the ISO `T` form before parsing, so a `type="date"` column renders
19
+ * and filters identically in both. Anything still unparseable comes back as an Invalid Date — every
20
+ * caller already guards with `Number.isNaN(date.getTime())`.
21
+ */
22
+ export declare function coerceToDate(value: unknown): Date;
23
+ export declare function formatDateValue(date: Date, format: string): string;
24
+ export declare function formatCellValue(value: unknown, type: InanduColumnType, format: string, locale: string, numberFormatter?: NumberFormatter): string;
@@ -0,0 +1,46 @@
1
+ export declare const en: {
2
+ MsgNoData: string;
3
+ MsgFirstPage: string;
4
+ MsgPreviousPage: string;
5
+ MsgNextPage: string;
6
+ MsgLastPage: string;
7
+ MsgSortBy: string;
8
+ MsgPageOf: string;
9
+ MsgFilterPlaceholder: string;
10
+ MsgFilterColumn: string;
11
+ MsgFilterMin: string;
12
+ MsgFilterMax: string;
13
+ MsgFilterFrom: string;
14
+ MsgFilterTo: string;
15
+ MsgAll: string;
16
+ MsgCancelFilter: string;
17
+ MsgCancelAllFilters: string;
18
+ MsgGroupByHint: string;
19
+ MsgGroupedBy: string;
20
+ MsgGroupHeader: string;
21
+ MsgCancelGrouping: string;
22
+ MsgSelectAll: string;
23
+ MsgSelectRow: string;
24
+ MsgExportCsv: string;
25
+ MsgExportExcel: string;
26
+ MsgExportPdf: string;
27
+ MsgPrint: string;
28
+ MsgToggleColumns: string;
29
+ MsgToggleColumn: string;
30
+ MsgEditCell: string;
31
+ MsgEditRow: string;
32
+ MsgSaveRow: string;
33
+ MsgCancelRowEdit: string;
34
+ MsgDeleteRow: string;
35
+ MsgAddRow: string;
36
+ MsgLoading: string;
37
+ MsgDragRow: string;
38
+ MsgAutosizeColumn: string;
39
+ MsgExpandDetail: string;
40
+ MsgCollapseDetail: string;
41
+ MsgDeleteSelected: string;
42
+ MsgValidationRequired: string;
43
+ MsgValidationMin: string;
44
+ MsgValidationMax: string;
45
+ MsgValidationPattern: string;
46
+ };
@@ -0,0 +1,46 @@
1
+ export declare const es: {
2
+ MsgNoData: string;
3
+ MsgFirstPage: string;
4
+ MsgPreviousPage: string;
5
+ MsgNextPage: string;
6
+ MsgLastPage: string;
7
+ MsgSortBy: string;
8
+ MsgPageOf: string;
9
+ MsgFilterPlaceholder: string;
10
+ MsgFilterColumn: string;
11
+ MsgFilterMin: string;
12
+ MsgFilterMax: string;
13
+ MsgFilterFrom: string;
14
+ MsgFilterTo: string;
15
+ MsgAll: string;
16
+ MsgCancelFilter: string;
17
+ MsgCancelAllFilters: string;
18
+ MsgGroupByHint: string;
19
+ MsgGroupedBy: string;
20
+ MsgGroupHeader: string;
21
+ MsgCancelGrouping: string;
22
+ MsgSelectAll: string;
23
+ MsgSelectRow: string;
24
+ MsgExportCsv: string;
25
+ MsgExportExcel: string;
26
+ MsgExportPdf: string;
27
+ MsgPrint: string;
28
+ MsgToggleColumns: string;
29
+ MsgToggleColumn: string;
30
+ MsgEditCell: string;
31
+ MsgEditRow: string;
32
+ MsgSaveRow: string;
33
+ MsgCancelRowEdit: string;
34
+ MsgDeleteRow: string;
35
+ MsgAddRow: string;
36
+ MsgLoading: string;
37
+ MsgDragRow: string;
38
+ MsgAutosizeColumn: string;
39
+ MsgExpandDetail: string;
40
+ MsgCollapseDetail: string;
41
+ MsgDeleteSelected: string;
42
+ MsgValidationRequired: string;
43
+ MsgValidationMin: string;
44
+ MsgValidationMax: string;
45
+ MsgValidationPattern: string;
46
+ };
@@ -0,0 +1,46 @@
1
+ export declare const fr: {
2
+ MsgNoData: string;
3
+ MsgFirstPage: string;
4
+ MsgPreviousPage: string;
5
+ MsgNextPage: string;
6
+ MsgLastPage: string;
7
+ MsgSortBy: string;
8
+ MsgPageOf: string;
9
+ MsgFilterPlaceholder: string;
10
+ MsgFilterColumn: string;
11
+ MsgFilterMin: string;
12
+ MsgFilterMax: string;
13
+ MsgFilterFrom: string;
14
+ MsgFilterTo: string;
15
+ MsgAll: string;
16
+ MsgCancelFilter: string;
17
+ MsgCancelAllFilters: string;
18
+ MsgGroupByHint: string;
19
+ MsgGroupedBy: string;
20
+ MsgGroupHeader: string;
21
+ MsgCancelGrouping: string;
22
+ MsgSelectAll: string;
23
+ MsgSelectRow: string;
24
+ MsgExportCsv: string;
25
+ MsgExportExcel: string;
26
+ MsgExportPdf: string;
27
+ MsgPrint: string;
28
+ MsgToggleColumns: string;
29
+ MsgToggleColumn: string;
30
+ MsgEditCell: string;
31
+ MsgEditRow: string;
32
+ MsgSaveRow: string;
33
+ MsgCancelRowEdit: string;
34
+ MsgDeleteRow: string;
35
+ MsgAddRow: string;
36
+ MsgLoading: string;
37
+ MsgDragRow: string;
38
+ MsgAutosizeColumn: string;
39
+ MsgExpandDetail: string;
40
+ MsgCollapseDetail: string;
41
+ MsgDeleteSelected: string;
42
+ MsgValidationRequired: string;
43
+ MsgValidationMin: string;
44
+ MsgValidationMax: string;
45
+ MsgValidationPattern: string;
46
+ };
@@ -0,0 +1,8 @@
1
+ import { en } from './en';
2
+ import { es } from './es';
3
+ import { fr } from './fr';
4
+ import { it } from './it';
5
+ import { zh } from './zh';
6
+ export { en, es, fr, it, zh };
7
+ /** Every built-in dictionary, keyed by primary language subtag. */
8
+ export declare const INANDU_GRID_TRANSLATIONS: Record<string, Record<string, string>>;
@@ -0,0 +1,46 @@
1
+ export declare const it: {
2
+ MsgNoData: string;
3
+ MsgFirstPage: string;
4
+ MsgPreviousPage: string;
5
+ MsgNextPage: string;
6
+ MsgLastPage: string;
7
+ MsgSortBy: string;
8
+ MsgPageOf: string;
9
+ MsgFilterPlaceholder: string;
10
+ MsgFilterColumn: string;
11
+ MsgFilterMin: string;
12
+ MsgFilterMax: string;
13
+ MsgFilterFrom: string;
14
+ MsgFilterTo: string;
15
+ MsgAll: string;
16
+ MsgCancelFilter: string;
17
+ MsgCancelAllFilters: string;
18
+ MsgGroupByHint: string;
19
+ MsgGroupedBy: string;
20
+ MsgGroupHeader: string;
21
+ MsgCancelGrouping: string;
22
+ MsgSelectAll: string;
23
+ MsgSelectRow: string;
24
+ MsgExportCsv: string;
25
+ MsgExportExcel: string;
26
+ MsgExportPdf: string;
27
+ MsgPrint: string;
28
+ MsgToggleColumns: string;
29
+ MsgToggleColumn: string;
30
+ MsgEditCell: string;
31
+ MsgEditRow: string;
32
+ MsgSaveRow: string;
33
+ MsgCancelRowEdit: string;
34
+ MsgDeleteRow: string;
35
+ MsgAddRow: string;
36
+ MsgLoading: string;
37
+ MsgDragRow: string;
38
+ MsgAutosizeColumn: string;
39
+ MsgExpandDetail: string;
40
+ MsgCollapseDetail: string;
41
+ MsgDeleteSelected: string;
42
+ MsgValidationRequired: string;
43
+ MsgValidationMin: string;
44
+ MsgValidationMax: string;
45
+ MsgValidationPattern: string;
46
+ };