ng-hub-ui-spreadsheet 22.0.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.
- package/LICENSE +21 -0
- package/README.md +571 -0
- package/fesm2022/ng-hub-ui-spreadsheet-signals.mjs +281 -0
- package/fesm2022/ng-hub-ui-spreadsheet-signals.mjs.map +1 -0
- package/fesm2022/ng-hub-ui-spreadsheet.mjs +4598 -0
- package/fesm2022/ng-hub-ui-spreadsheet.mjs.map +1 -0
- package/package.json +67 -0
- package/types/ng-hub-ui-spreadsheet-signals.d.ts +125 -0
- package/types/ng-hub-ui-spreadsheet.d.ts +1958 -0
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ng-hub-ui-spreadsheet",
|
|
3
|
+
"version": "22.0.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "Excel-like editable spreadsheet for Angular: in-cell editing, full keyboard navigation, rectangular range selection and a clipboard that round-trips with Excel. Accessible, themable through CSS variables and free of paid tiers. Part of the ng-hub-ui family.",
|
|
6
|
+
"author": "Carlos Morcillo Fernández <carlos.morcillo@me.com> (https://www.carlosmorcillo.com)",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/hub-env/ng-hub-ui-spreadsheet.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/hub-env/hub-ui/issues"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://hubui.dev/en/spreadsheet/overview/",
|
|
15
|
+
"keywords": [
|
|
16
|
+
"angular",
|
|
17
|
+
"spreadsheet",
|
|
18
|
+
"data-grid",
|
|
19
|
+
"editable-table",
|
|
20
|
+
"excel",
|
|
21
|
+
"clipboard",
|
|
22
|
+
"cell-editing",
|
|
23
|
+
"range-selection",
|
|
24
|
+
"accessibility",
|
|
25
|
+
"ng-hub-ui",
|
|
26
|
+
"standalone",
|
|
27
|
+
"signals"
|
|
28
|
+
],
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"@angular/common": ">=22.0.0",
|
|
31
|
+
"@angular/core": ">=22.0.0",
|
|
32
|
+
"ng-hub-ui-ds": ">=22.0.0",
|
|
33
|
+
"ng-hub-ui-utils": ">=22.18.0",
|
|
34
|
+
"@angular/forms": ">=22.0.0"
|
|
35
|
+
},
|
|
36
|
+
"peerDependenciesMeta": {
|
|
37
|
+
"ng-hub-ui-ds": {
|
|
38
|
+
"optional": true
|
|
39
|
+
},
|
|
40
|
+
"@angular/forms": {
|
|
41
|
+
"optional": true
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"tslib": "^2.3.0"
|
|
46
|
+
},
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
},
|
|
50
|
+
"sideEffects": false,
|
|
51
|
+
"module": "fesm2022/ng-hub-ui-spreadsheet.mjs",
|
|
52
|
+
"typings": "types/ng-hub-ui-spreadsheet.d.ts",
|
|
53
|
+
"exports": {
|
|
54
|
+
"./package.json": {
|
|
55
|
+
"default": "./package.json"
|
|
56
|
+
},
|
|
57
|
+
".": {
|
|
58
|
+
"types": "./types/ng-hub-ui-spreadsheet.d.ts",
|
|
59
|
+
"default": "./fesm2022/ng-hub-ui-spreadsheet.mjs"
|
|
60
|
+
},
|
|
61
|
+
"./signals": {
|
|
62
|
+
"types": "./types/ng-hub-ui-spreadsheet-signals.d.ts",
|
|
63
|
+
"default": "./fesm2022/ng-hub-ui-spreadsheet-signals.mjs"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"type": "module"
|
|
67
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import * as _angular_core from '@angular/core';
|
|
2
|
+
import { FormValueControl, ValidationError, FieldTree } from '@angular/forms/signals';
|
|
3
|
+
import { HubGridSpan, HubGridRange } from 'ng-hub-ui-utils';
|
|
4
|
+
import { HubSpreadsheetColumn, HubSpreadsheetCellState, HubSpreadsheetCommit, HubSpreadsheetPaste, HubSpreadsheetCellRef } from 'ng-hub-ui-spreadsheet';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A sheet that is a form control: its value is the rows.
|
|
8
|
+
*
|
|
9
|
+
* Bind it with the `FormField` directive and the rows become the field's value, written back on
|
|
10
|
+
* every edit, paste and clearing. Because the contract is Angular's own
|
|
11
|
+
* {@link FormValueControl}, the same component also works inside reactive and template-driven
|
|
12
|
+
* forms — Angular's migration guide says as much, and forbids implementing the old
|
|
13
|
+
* `ControlValueAccessor` alongside it.
|
|
14
|
+
*
|
|
15
|
+
* **What this shape cannot do, and why it is not the only one.** A control bound to a collection
|
|
16
|
+
* receives that field's own errors and never its descendants'. A rule declared with `applyEach`
|
|
17
|
+
* on the rows therefore produces errors this component will not see, and per-cell validation is
|
|
18
|
+
* the whole point of an editable sheet. When you need it, keep `<hub-spreadsheet>` bound to your
|
|
19
|
+
* own rows and feed its `errors` input — `spreadsheetFieldErrors()` maps a field tree onto the
|
|
20
|
+
* map it expects.
|
|
21
|
+
*/
|
|
22
|
+
declare class HubSpreadsheetFieldComponent<TRow extends object> implements FormValueControl<TRow[]> {
|
|
23
|
+
/** The rows, as the field's value. Written back whenever the reader changes a cell. */
|
|
24
|
+
readonly value: _angular_core.ModelSignal<TRow[]>;
|
|
25
|
+
/** What each column shows and allows. */
|
|
26
|
+
readonly columns: _angular_core.InputSignal<readonly HubSpreadsheetColumn<TRow>[]>;
|
|
27
|
+
/** A stable name per row. */
|
|
28
|
+
readonly rowKey: _angular_core.InputSignal<(row: TRow) => string>;
|
|
29
|
+
/** The save state of each cell. */
|
|
30
|
+
readonly states: _angular_core.InputSignal<Record<string, HubSpreadsheetCellState>>;
|
|
31
|
+
/** The character this reader types decimals with. */
|
|
32
|
+
readonly decimalMark: _angular_core.InputSignal<"," | ".">;
|
|
33
|
+
/** What an empty sheet says. */
|
|
34
|
+
readonly emptyText: _angular_core.InputSignal<string>;
|
|
35
|
+
/** How many rows Page Up and Page Down travel. */
|
|
36
|
+
readonly pageSize: _angular_core.InputSignal<number>;
|
|
37
|
+
/** Turns off every editor, whatever the cells say. */
|
|
38
|
+
readonly readonly: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
39
|
+
/** How many columns stay pinned to the leading edge. */
|
|
40
|
+
readonly frozenColumns: _angular_core.InputSignal<number>;
|
|
41
|
+
/** How many rows stay pinned below the header. */
|
|
42
|
+
readonly frozenRows: _angular_core.InputSignal<number>;
|
|
43
|
+
/** Merged blocks. */
|
|
44
|
+
readonly spans: _angular_core.InputSignal<readonly HubGridSpan[]>;
|
|
45
|
+
/**
|
|
46
|
+
* Bound by the `FormField` directive. Only the array field's own errors arrive here, which is
|
|
47
|
+
* why they are shown as a whole rather than attributed to a cell.
|
|
48
|
+
*/
|
|
49
|
+
readonly errors: _angular_core.InputSignal<readonly ValidationError.WithOptionalFieldTree[]>;
|
|
50
|
+
/** Bound by the `FormField` directive; disables every editor. */
|
|
51
|
+
readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
52
|
+
/** The selected rectangle changed. */
|
|
53
|
+
readonly selectionChange: _angular_core.OutputEmitterRef<HubGridRange | null>;
|
|
54
|
+
/**
|
|
55
|
+
* The sheet's own per-cell error map.
|
|
56
|
+
*
|
|
57
|
+
* Empty here on purpose: this shape has no per-cell errors to give. It exists so the inner
|
|
58
|
+
* component's input is always bound to something, and so a future release can fill it without
|
|
59
|
+
* changing the template.
|
|
60
|
+
*/
|
|
61
|
+
protected readonly cellErrors: _angular_core.Signal<Record<string, string>>;
|
|
62
|
+
protected onCommit(change: HubSpreadsheetCommit<TRow>): void;
|
|
63
|
+
protected onPaste(paste: HubSpreadsheetPaste<TRow>): void;
|
|
64
|
+
protected onCleared(cells: readonly HubSpreadsheetCellRef<TRow>[]): void;
|
|
65
|
+
private write;
|
|
66
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<HubSpreadsheetFieldComponent<any>, never>;
|
|
67
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<HubSpreadsheetFieldComponent<any>, "hub-spreadsheet-field", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "columns": { "alias": "columns"; "required": true; "isSignal": true; }; "rowKey": { "alias": "rowKey"; "required": true; "isSignal": true; }; "states": { "alias": "states"; "required": false; "isSignal": true; }; "decimalMark": { "alias": "decimalMark"; "required": false; "isSignal": true; }; "emptyText": { "alias": "emptyText"; "required": false; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "frozenColumns": { "alias": "frozenColumns"; "required": false; "isSignal": true; }; "frozenRows": { "alias": "frozenRows"; "required": false; "isSignal": true; }; "spans": { "alias": "spans"; "required": false; "isSignal": true; }; "errors": { "alias": "errors"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "selectionChange": "selectionChange"; }, never, never, true, never>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Turns a field tree over rows into the per-cell error map `<hub-spreadsheet>` reads.
|
|
72
|
+
*
|
|
73
|
+
* This is the piece that makes per-cell validation work, and it exists because of a limit in the
|
|
74
|
+
* framework rather than a preference: a control bound to a collection is handed that field's own
|
|
75
|
+
* errors and never its descendants'. Rules declared with `applyEach` therefore land on the row
|
|
76
|
+
* fields, one level below anything the sheet could see for itself.
|
|
77
|
+
*
|
|
78
|
+
* So the host keeps the field tree, and this walks it: for each row and each column, it reads
|
|
79
|
+
* that field's errors and writes the first message under the key the sheet addresses cells by.
|
|
80
|
+
*
|
|
81
|
+
* ```ts
|
|
82
|
+
* readonly form = form(this.lines, (path) => {
|
|
83
|
+
* applyEach(path, (line) => {
|
|
84
|
+
* min(line.units, 1, { message: 'At least one unit' });
|
|
85
|
+
* });
|
|
86
|
+
* });
|
|
87
|
+
*
|
|
88
|
+
* protected readonly cellErrors = computed(() =>
|
|
89
|
+
* spreadsheetFieldErrors(this.form, this.lines(), this.columns, (row) => row.id)
|
|
90
|
+
* );
|
|
91
|
+
* ```
|
|
92
|
+
*
|
|
93
|
+
* @param form The field tree over the rows.
|
|
94
|
+
* @param rows The rows themselves, in the same order.
|
|
95
|
+
* @param columns The sheet's columns; only aliases that name a field are looked up.
|
|
96
|
+
* @param rowKey How the sheet names a row, so the keys line up with `states`.
|
|
97
|
+
* @param touchedOnly Whether to report only fields the reader has visited. True by default: an
|
|
98
|
+
* untouched form that paints itself red the moment it renders is a form nobody trusts.
|
|
99
|
+
*/
|
|
100
|
+
declare function spreadsheetFieldErrors<TRow extends object>(form: FieldTree<TRow[]>, rows: readonly TRow[], columns: readonly HubSpreadsheetColumn<TRow>[], rowKey: (row: TRow) => string, touchedOnly?: boolean): Record<string, string>;
|
|
101
|
+
/**
|
|
102
|
+
* Marks the field behind a cell as visited.
|
|
103
|
+
*
|
|
104
|
+
* Errors are held back until a cell has been touched, and a sheet has no way to report that on
|
|
105
|
+
* its own: the `FormField` directive learns it from a control's `touch` output, and a sheet
|
|
106
|
+
* driven through `errors` is not bound to a field at all. So the host says it, from the
|
|
107
|
+
* `commit`, `pasted` and `cleared` handlers it already writes.
|
|
108
|
+
*
|
|
109
|
+
* Without this call, `spreadsheetFieldErrors()` in its default mode reports nothing and no cell
|
|
110
|
+
* is ever marked — which looks exactly like validation that does not work.
|
|
111
|
+
*
|
|
112
|
+
* ```ts
|
|
113
|
+
* protected onCommit(change: HubSpreadsheetCommit<Line>): void {
|
|
114
|
+
* this.lines.update(...);
|
|
115
|
+
* touchSpreadsheetField(this.orderForm, change.coords.row, change.column.key);
|
|
116
|
+
* }
|
|
117
|
+
* ```
|
|
118
|
+
*
|
|
119
|
+
* @param form The field tree over the rows.
|
|
120
|
+
* @param row The row index of the cell.
|
|
121
|
+
* @param columnKey The column's alias.
|
|
122
|
+
*/
|
|
123
|
+
declare function touchSpreadsheetField<TRow extends object>(form: FieldTree<TRow[]>, row: number, columnKey: string): void;
|
|
124
|
+
|
|
125
|
+
export { HubSpreadsheetFieldComponent, spreadsheetFieldErrors, touchSpreadsheetField };
|