@candidstartup/simple-spreadsheet-data 0.8.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 +29 -0
- package/README.md +10 -0
- package/dist/index.d.ts +99 -0
- package/dist/index.js +184 -0
- package/dist/index.js.map +1 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023-2024, Tim Wiegand
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
[](https://www.npmjs.com/package/@candidstartup/simple-spreadsheet-data)
|
|
2
|
+
[](https://www.npmjs.com/package/@candidstartup/simple-spreadsheet-data)
|
|
3
|
+
[](https://www.npmjs.com/package/@candidstartup/simple-spreadsheet-data)
|
|
4
|
+
[](https://github.com/TheCandidStartup/infinisheet/actions/workflows/build.yml)
|
|
5
|
+
|
|
6
|
+
[GitHub](https://github.com/TheCandidStartup/infinisheet/tree/main/packages/simple-spreadsheet-data) | [NPM](https://www.npmjs.com/package/@candidstartup/simple-spreadsheet-data) | [API](https://www.thecandidstartup.org/infinisheet/modules/_candidstartup_simple-spreadsheet-data.html)
|
|
7
|
+
|
|
8
|
+
# @candidstartup/simple-spreadsheet-data
|
|
9
|
+
|
|
10
|
+
Reference implementations of `SpreadsheetData`
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { SpreadsheetData, ItemOffsetMapping, CellValue } from '@candidstartup/infinisheet-types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Branding Enum. Used by {@link SimpleSnapshot} to ensure that
|
|
5
|
+
* you'll get a type error if you pass some random object where a `SimpleSnapshot`
|
|
6
|
+
* is expected.
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
declare enum _SimpleSnapshotBrand {
|
|
10
|
+
_DO_NOT_USE = ""
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Opaque type representing a {@link SimpleSpreadsheetData} snapshot. All the
|
|
14
|
+
* internal implementation details are hidden from the exported API.
|
|
15
|
+
*/
|
|
16
|
+
interface SimpleSnapshot {
|
|
17
|
+
/** @internal */
|
|
18
|
+
_brand: _SimpleSnapshotBrand;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Reference implementation of {@link SpreadsheetData}
|
|
22
|
+
*
|
|
23
|
+
* Editable in-memory spreadsheet data container. Starts out empty. Use the API to fill
|
|
24
|
+
* with data!
|
|
25
|
+
*
|
|
26
|
+
* Intended for use as a mock, to compare with an optimized implementation when testing and
|
|
27
|
+
* for simple sample apps. Simplest possible implementation, no attempt at optimization.
|
|
28
|
+
*/
|
|
29
|
+
declare class SimpleSpreadsheetData implements SpreadsheetData<SimpleSnapshot> {
|
|
30
|
+
#private;
|
|
31
|
+
constructor();
|
|
32
|
+
subscribe(onDataChange: () => void): () => void;
|
|
33
|
+
getSnapshot(): SimpleSnapshot;
|
|
34
|
+
getRowCount(snapshot: SimpleSnapshot): number;
|
|
35
|
+
getRowItemOffsetMapping(_snapshot: SimpleSnapshot): ItemOffsetMapping;
|
|
36
|
+
getColumnCount(snapshot: SimpleSnapshot): number;
|
|
37
|
+
getColumnItemOffsetMapping(_snapshot: SimpleSnapshot): ItemOffsetMapping;
|
|
38
|
+
getCellValue(snapshot: SimpleSnapshot, row: number, column: number): CellValue;
|
|
39
|
+
getCellFormat(snapshot: SimpleSnapshot, row: number, column: number): string | undefined;
|
|
40
|
+
setCellValueAndFormat(row: number, column: number, value: CellValue, format: string | undefined): boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Branding Enum. Used by {@link LayeredSnapshot} to ensure that
|
|
45
|
+
* you'll get a type error if you pass some random object where a `LayeredSnapshot`
|
|
46
|
+
* is expected.
|
|
47
|
+
* @internal
|
|
48
|
+
*/
|
|
49
|
+
declare enum _LayeredSnapshotBrand {
|
|
50
|
+
_DO_NOT_USE = ""
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Opaque type representing a {@link LayeredSpreadsheetData} snapshot. All the
|
|
54
|
+
* internal implementation details are hidden from the exported API.
|
|
55
|
+
*/
|
|
56
|
+
interface LayeredSnapshot<BaseSnapshot, EditSnapshot> {
|
|
57
|
+
/** @internal */
|
|
58
|
+
_brand: [_LayeredSnapshotBrand, BaseSnapshot, EditSnapshot];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Extracts the snapshot type from a {@link SpreadsheetData} instance
|
|
62
|
+
*/
|
|
63
|
+
type SnapshotType<T> = T extends SpreadsheetData<infer TResult> ? TResult : never;
|
|
64
|
+
/**
|
|
65
|
+
* Implementation of {@link SpreadsheetData} that layers two other `SpreadsheetData` instances on top of each other.
|
|
66
|
+
*
|
|
67
|
+
* There's an "edit" layer on top where any changes are stored, with a "base" layer underneath. If a value is `undefined` in the edit layer,
|
|
68
|
+
* the corresponding value is returned from the base layer instead.
|
|
69
|
+
*
|
|
70
|
+
* Common use case is a read only reference data source as the base layer with an initially empty edit layer that accepts changes.
|
|
71
|
+
*
|
|
72
|
+
* @typeParam BaseData - Type of base layer `SpreadsheetData` instance
|
|
73
|
+
* @typeParam EditData - Type of edit layer `SpreadsheetData` instance
|
|
74
|
+
* @typeParam BaseSnapshot - Type of snapshot used by `BaseData`. By default, inferred from `BaseData`.
|
|
75
|
+
* @typeParam EditSnapshot - Type of snapshot used by `EditData`. By default, inferred from `EditData`.
|
|
76
|
+
*/
|
|
77
|
+
declare class LayeredSpreadsheetData<BaseData extends SpreadsheetData<BaseSnapshot>, EditData extends SpreadsheetData<EditSnapshot>, BaseSnapshot = SnapshotType<BaseData>, EditSnapshot = SnapshotType<EditData>> implements SpreadsheetData<LayeredSnapshot<BaseSnapshot, EditSnapshot>> {
|
|
78
|
+
#private;
|
|
79
|
+
/**
|
|
80
|
+
* Creates a `LayeredSpreadsheetData` instance from two existing `SpreadsheetData` instances.
|
|
81
|
+
*
|
|
82
|
+
* All type parameters can be inferred from the constructor arguments.
|
|
83
|
+
*
|
|
84
|
+
* @param base - `SpreadsheetData` instance to use for the base layer
|
|
85
|
+
* @param edit - `SpreadsheetData` instance to use for the edit layer
|
|
86
|
+
*/
|
|
87
|
+
constructor(base: BaseData, edit: EditData);
|
|
88
|
+
subscribe(onDataChange: () => void): () => void;
|
|
89
|
+
getSnapshot(): LayeredSnapshot<BaseSnapshot, EditSnapshot>;
|
|
90
|
+
getRowCount(snapshot: LayeredSnapshot<BaseSnapshot, EditSnapshot>): number;
|
|
91
|
+
getRowItemOffsetMapping(snapshot: LayeredSnapshot<BaseSnapshot, EditSnapshot>): ItemOffsetMapping;
|
|
92
|
+
getColumnCount(snapshot: LayeredSnapshot<BaseSnapshot, EditSnapshot>): number;
|
|
93
|
+
getColumnItemOffsetMapping(snapshot: LayeredSnapshot<BaseSnapshot, EditSnapshot>): ItemOffsetMapping;
|
|
94
|
+
getCellValue(snapshot: LayeredSnapshot<BaseSnapshot, EditSnapshot>, row: number, column: number): CellValue;
|
|
95
|
+
getCellFormat(snapshot: LayeredSnapshot<BaseSnapshot, EditSnapshot>, row: number, column: number): string | undefined;
|
|
96
|
+
setCellValueAndFormat(row: number, column: number, value: CellValue, format: string | undefined): boolean;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export { type LayeredSnapshot, LayeredSpreadsheetData, type SimpleSnapshot, SimpleSpreadsheetData, type SnapshotType, _LayeredSnapshotBrand, _SimpleSnapshotBrand };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { FixedSizeItemOffsetMapping, rowColCoordsToRef } from '@candidstartup/infinisheet-types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Branding Enum. Used by {@link SimpleSnapshot} to ensure that
|
|
5
|
+
* you'll get a type error if you pass some random object where a `SimpleSnapshot`
|
|
6
|
+
* is expected.
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
var _SimpleSnapshotBrand;
|
|
10
|
+
(function (_SimpleSnapshotBrand) {
|
|
11
|
+
_SimpleSnapshotBrand["_DO_NOT_USE"] = "";
|
|
12
|
+
})(_SimpleSnapshotBrand || (_SimpleSnapshotBrand = {}));
|
|
13
|
+
const rowItemOffsetMapping = new FixedSizeItemOffsetMapping(30);
|
|
14
|
+
const columnItemOffsetMapping = new FixedSizeItemOffsetMapping(100);
|
|
15
|
+
function asContent$1(snapshot) {
|
|
16
|
+
return snapshot;
|
|
17
|
+
}
|
|
18
|
+
function asSnapshot$1(snapshot) {
|
|
19
|
+
return snapshot;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Reference implementation of {@link SpreadsheetData}
|
|
23
|
+
*
|
|
24
|
+
* Editable in-memory spreadsheet data container. Starts out empty. Use the API to fill
|
|
25
|
+
* with data!
|
|
26
|
+
*
|
|
27
|
+
* Intended for use as a mock, to compare with an optimized implementation when testing and
|
|
28
|
+
* for simple sample apps. Simplest possible implementation, no attempt at optimization.
|
|
29
|
+
*/
|
|
30
|
+
class SimpleSpreadsheetData {
|
|
31
|
+
constructor() {
|
|
32
|
+
this.#listeners = [];
|
|
33
|
+
this.#content = {
|
|
34
|
+
values: {},
|
|
35
|
+
rowCount: 0,
|
|
36
|
+
colCount: 0
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
subscribe(onDataChange) {
|
|
40
|
+
this.#listeners = [...this.#listeners, onDataChange];
|
|
41
|
+
return () => {
|
|
42
|
+
this.#listeners = this.#listeners.filter(l => l !== onDataChange);
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
getSnapshot() {
|
|
46
|
+
return asSnapshot$1(this.#content);
|
|
47
|
+
}
|
|
48
|
+
getRowCount(snapshot) {
|
|
49
|
+
return asContent$1(snapshot).rowCount;
|
|
50
|
+
}
|
|
51
|
+
getRowItemOffsetMapping(_snapshot) {
|
|
52
|
+
return rowItemOffsetMapping;
|
|
53
|
+
}
|
|
54
|
+
getColumnCount(snapshot) {
|
|
55
|
+
return asContent$1(snapshot).colCount;
|
|
56
|
+
}
|
|
57
|
+
getColumnItemOffsetMapping(_snapshot) {
|
|
58
|
+
return columnItemOffsetMapping;
|
|
59
|
+
}
|
|
60
|
+
getCellValue(snapshot, row, column) {
|
|
61
|
+
const ref = rowColCoordsToRef(row, column);
|
|
62
|
+
return asContent$1(snapshot).values[ref]?.value;
|
|
63
|
+
}
|
|
64
|
+
getCellFormat(snapshot, row, column) {
|
|
65
|
+
const ref = rowColCoordsToRef(row, column);
|
|
66
|
+
return asContent$1(snapshot).values[ref]?.format;
|
|
67
|
+
}
|
|
68
|
+
setCellValueAndFormat(row, column, value, format) {
|
|
69
|
+
const curr = this.#content;
|
|
70
|
+
const ref = rowColCoordsToRef(row, column);
|
|
71
|
+
// Snapshot semantics preserved by treating SimpleSnapshot as an immutable data structure which is
|
|
72
|
+
// replaced with a modified copy on every update. Yes, this is horribly inefficient.
|
|
73
|
+
// For simplicity, setting a value to undefined stores it explicitly rather than removing it. Functional
|
|
74
|
+
// behavior is the same.
|
|
75
|
+
this.#content = {
|
|
76
|
+
values: { ...curr.values, [ref]: { value, format } },
|
|
77
|
+
rowCount: Math.max(curr.rowCount, row + 1),
|
|
78
|
+
colCount: Math.max(curr.colCount, column + 1)
|
|
79
|
+
};
|
|
80
|
+
this.#notifyListeners();
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
#notifyListeners() {
|
|
84
|
+
for (const listener of this.#listeners)
|
|
85
|
+
listener();
|
|
86
|
+
}
|
|
87
|
+
#listeners;
|
|
88
|
+
#content;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Branding Enum. Used by {@link LayeredSnapshot} to ensure that
|
|
93
|
+
* you'll get a type error if you pass some random object where a `LayeredSnapshot`
|
|
94
|
+
* is expected.
|
|
95
|
+
* @internal
|
|
96
|
+
*/
|
|
97
|
+
var _LayeredSnapshotBrand;
|
|
98
|
+
(function (_LayeredSnapshotBrand) {
|
|
99
|
+
_LayeredSnapshotBrand["_DO_NOT_USE"] = "";
|
|
100
|
+
})(_LayeredSnapshotBrand || (_LayeredSnapshotBrand = {}));
|
|
101
|
+
function asContent(snapshot) {
|
|
102
|
+
return snapshot;
|
|
103
|
+
}
|
|
104
|
+
function asSnapshot(snapshot) {
|
|
105
|
+
return snapshot;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Implementation of {@link SpreadsheetData} that layers two other `SpreadsheetData` instances on top of each other.
|
|
109
|
+
*
|
|
110
|
+
* There's an "edit" layer on top where any changes are stored, with a "base" layer underneath. If a value is `undefined` in the edit layer,
|
|
111
|
+
* the corresponding value is returned from the base layer instead.
|
|
112
|
+
*
|
|
113
|
+
* Common use case is a read only reference data source as the base layer with an initially empty edit layer that accepts changes.
|
|
114
|
+
*
|
|
115
|
+
* @typeParam BaseData - Type of base layer `SpreadsheetData` instance
|
|
116
|
+
* @typeParam EditData - Type of edit layer `SpreadsheetData` instance
|
|
117
|
+
* @typeParam BaseSnapshot - Type of snapshot used by `BaseData`. By default, inferred from `BaseData`.
|
|
118
|
+
* @typeParam EditSnapshot - Type of snapshot used by `EditData`. By default, inferred from `EditData`.
|
|
119
|
+
*/
|
|
120
|
+
class LayeredSpreadsheetData {
|
|
121
|
+
/**
|
|
122
|
+
* Creates a `LayeredSpreadsheetData` instance from two existing `SpreadsheetData` instances.
|
|
123
|
+
*
|
|
124
|
+
* All type parameters can be inferred from the constructor arguments.
|
|
125
|
+
*
|
|
126
|
+
* @param base - `SpreadsheetData` instance to use for the base layer
|
|
127
|
+
* @param edit - `SpreadsheetData` instance to use for the edit layer
|
|
128
|
+
*/
|
|
129
|
+
constructor(base, edit) {
|
|
130
|
+
this.#base = base;
|
|
131
|
+
this.#edit = edit;
|
|
132
|
+
}
|
|
133
|
+
subscribe(onDataChange) {
|
|
134
|
+
const unsubscribeBase = this.#base.subscribe(onDataChange);
|
|
135
|
+
const unsubscribeEdit = this.#edit.subscribe(onDataChange);
|
|
136
|
+
return () => {
|
|
137
|
+
unsubscribeBase();
|
|
138
|
+
unsubscribeEdit();
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
getSnapshot() {
|
|
142
|
+
const baseSnapshot = this.#base.getSnapshot();
|
|
143
|
+
const editSnapshot = this.#edit.getSnapshot();
|
|
144
|
+
if (!this.#content || this.#content.base != baseSnapshot || this.#content.edit != editSnapshot) {
|
|
145
|
+
this.#content = { base: baseSnapshot, edit: editSnapshot };
|
|
146
|
+
}
|
|
147
|
+
return asSnapshot(this.#content);
|
|
148
|
+
}
|
|
149
|
+
getRowCount(snapshot) {
|
|
150
|
+
const content = asContent(snapshot);
|
|
151
|
+
return Math.max(this.#base.getRowCount(content.base), this.#edit.getRowCount(content.edit));
|
|
152
|
+
}
|
|
153
|
+
getRowItemOffsetMapping(snapshot) {
|
|
154
|
+
return this.#base.getRowItemOffsetMapping(asContent(snapshot).base);
|
|
155
|
+
}
|
|
156
|
+
getColumnCount(snapshot) {
|
|
157
|
+
const content = asContent(snapshot);
|
|
158
|
+
return Math.max(this.#base.getColumnCount(content.base), this.#edit.getColumnCount(content.edit));
|
|
159
|
+
}
|
|
160
|
+
getColumnItemOffsetMapping(snapshot) {
|
|
161
|
+
return this.#base.getColumnItemOffsetMapping(asContent(snapshot).base);
|
|
162
|
+
}
|
|
163
|
+
getCellValue(snapshot, row, column) {
|
|
164
|
+
const content = asContent(snapshot);
|
|
165
|
+
const editValue = this.#edit.getCellValue(content.edit, row, column);
|
|
166
|
+
if (editValue !== undefined)
|
|
167
|
+
return editValue;
|
|
168
|
+
return this.#base.getCellValue(content.base, row, column);
|
|
169
|
+
}
|
|
170
|
+
getCellFormat(snapshot, row, column) {
|
|
171
|
+
const content = asContent(snapshot);
|
|
172
|
+
const editValue = this.#edit.getCellValue(content.edit, row, column);
|
|
173
|
+
return (editValue === undefined) ? this.#base.getCellFormat(content.base, row, column) : this.#edit.getCellFormat(content.edit, row, column);
|
|
174
|
+
}
|
|
175
|
+
setCellValueAndFormat(row, column, value, format) {
|
|
176
|
+
return this.#edit.setCellValueAndFormat(row, column, value, format);
|
|
177
|
+
}
|
|
178
|
+
#base;
|
|
179
|
+
#edit;
|
|
180
|
+
#content;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export { LayeredSpreadsheetData, SimpleSpreadsheetData, _LayeredSnapshotBrand, _SimpleSnapshotBrand };
|
|
184
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/SimpleSpreadsheetData.ts","../src/LayeredSpreadsheetData.ts"],"sourcesContent":["import type { CellValue, SpreadsheetData, RowColRef, ItemOffsetMapping } from \"@candidstartup/infinisheet-types\";\nimport { FixedSizeItemOffsetMapping, rowColCoordsToRef } from \"@candidstartup/infinisheet-types\";\n\ninterface CellContent {\n value: CellValue;\n format: string|undefined;\n}\n\ninterface SimpleSnapshotContent {\n values: Record<RowColRef,CellContent>;\n rowCount: number;\n colCount: number;\n}\n\n/** \n * Branding Enum. Used by {@link SimpleSnapshot} to ensure that\n * you'll get a type error if you pass some random object where a `SimpleSnapshot`\n * is expected.\n * @internal\n */\nexport enum _SimpleSnapshotBrand { _DO_NOT_USE=\"\" };\n\n/**\n * Opaque type representing a {@link SimpleSpreadsheetData} snapshot. All the\n * internal implementation details are hidden from the exported API.\n */\nexport interface SimpleSnapshot {\n /** @internal */\n _brand: _SimpleSnapshotBrand;\n}\n\nconst rowItemOffsetMapping = new FixedSizeItemOffsetMapping(30);\nconst columnItemOffsetMapping = new FixedSizeItemOffsetMapping(100);\n\nfunction asContent(snapshot: SimpleSnapshot) {\n return snapshot as unknown as SimpleSnapshotContent;\n}\n\nfunction asSnapshot(snapshot: SimpleSnapshotContent) {\n return snapshot as unknown as SimpleSnapshot;\n}\n\n/**\n * Reference implementation of {@link SpreadsheetData}\n * \n * Editable in-memory spreadsheet data container. Starts out empty. Use the API to fill\n * with data!\n * \n * Intended for use as a mock, to compare with an optimized implementation when testing and\n * for simple sample apps. Simplest possible implementation, no attempt at optimization.\n */\nexport class SimpleSpreadsheetData implements SpreadsheetData<SimpleSnapshot> {\n constructor () {\n this.#listeners = [];\n this.#content = {\n values: {},\n rowCount: 0,\n colCount: 0\n }\n }\n\n subscribe(onDataChange: () => void): () => void {\n this.#listeners = [...this.#listeners, onDataChange];\n return () => {\n this.#listeners = this.#listeners.filter(l => l !== onDataChange);\n }\n }\n\n getSnapshot(): SimpleSnapshot {\n return asSnapshot(this.#content);\n }\n\n getRowCount(snapshot: SimpleSnapshot): number {\n return asContent(snapshot).rowCount;\n }\n\n getRowItemOffsetMapping(_snapshot: SimpleSnapshot): ItemOffsetMapping {\n return rowItemOffsetMapping;\n }\n\n getColumnCount(snapshot: SimpleSnapshot): number {\n return asContent(snapshot).colCount;\n }\n\n getColumnItemOffsetMapping(_snapshot: SimpleSnapshot): ItemOffsetMapping {\n return columnItemOffsetMapping\n }\n\n getCellValue(snapshot: SimpleSnapshot, row: number, column: number): CellValue {\n const ref = rowColCoordsToRef(row, column);\n return asContent(snapshot).values[ref]?.value;\n }\n\n getCellFormat(snapshot: SimpleSnapshot, row: number, column: number): string | undefined {\n const ref = rowColCoordsToRef(row, column);\n return asContent(snapshot).values[ref]?.format;\n }\n\n setCellValueAndFormat(row: number, column: number, value: CellValue, format: string | undefined): boolean {\n const curr = this.#content;\n const ref = rowColCoordsToRef(row, column);\n\n // Snapshot semantics preserved by treating SimpleSnapshot as an immutable data structure which is \n // replaced with a modified copy on every update. Yes, this is horribly inefficient. \n // For simplicity, setting a value to undefined stores it explicitly rather than removing it. Functional\n // behavior is the same.\n this.#content = {\n values: { ...curr.values, [ref]: { value, format }},\n rowCount: Math.max(curr.rowCount, row+1),\n colCount: Math.max(curr.colCount, column+1)\n }\n this.#notifyListeners();\n\n return true;\n }\n\n #notifyListeners() {\n for (const listener of this.#listeners)\n listener();\n }\n\n #listeners: (() => void)[];\n #content: SimpleSnapshotContent;\n}\n","import type { CellValue, SpreadsheetData, ItemOffsetMapping } from \"@candidstartup/infinisheet-types\";\n\ninterface LayeredSnapshotContent<BaseSnapshot, EditSnapshot> {\n base: BaseSnapshot,\n edit: EditSnapshot\n}\n\n/** \n * Branding Enum. Used by {@link LayeredSnapshot} to ensure that\n * you'll get a type error if you pass some random object where a `LayeredSnapshot`\n * is expected.\n * @internal\n */\nexport enum _LayeredSnapshotBrand { _DO_NOT_USE=\"\" };\n\n/**\n * Opaque type representing a {@link LayeredSpreadsheetData} snapshot. All the\n * internal implementation details are hidden from the exported API.\n */\nexport interface LayeredSnapshot<BaseSnapshot,EditSnapshot> {\n /** @internal */\n _brand: [ _LayeredSnapshotBrand, BaseSnapshot, EditSnapshot ]\n}\n\nfunction asContent<Base,Edit>(snapshot: LayeredSnapshot<Base,Edit>) {\n return snapshot as unknown as LayeredSnapshotContent<Base,Edit>;\n}\n\nfunction asSnapshot<Base,Edit>(snapshot: LayeredSnapshotContent<Base,Edit>) {\n return snapshot as unknown as LayeredSnapshot<Base,Edit>;\n}\n\n/**\n * Extracts the snapshot type from a {@link SpreadsheetData} instance\n */\nexport type SnapshotType<T> = T extends SpreadsheetData<infer TResult> ? TResult : never;\n\n/**\n * Implementation of {@link SpreadsheetData} that layers two other `SpreadsheetData` instances on top of each other.\n * \n * There's an \"edit\" layer on top where any changes are stored, with a \"base\" layer underneath. If a value is `undefined` in the edit layer, \n * the corresponding value is returned from the base layer instead.\n * \n * Common use case is a read only reference data source as the base layer with an initially empty edit layer that accepts changes.\n * \n * @typeParam BaseData - Type of base layer `SpreadsheetData` instance\n * @typeParam EditData - Type of edit layer `SpreadsheetData` instance\n * @typeParam BaseSnapshot - Type of snapshot used by `BaseData`. By default, inferred from `BaseData`.\n * @typeParam EditSnapshot - Type of snapshot used by `EditData`. By default, inferred from `EditData`.\n */\nexport class LayeredSpreadsheetData<BaseData extends SpreadsheetData<BaseSnapshot>, \n EditData extends SpreadsheetData<EditSnapshot>, BaseSnapshot = SnapshotType<BaseData>, EditSnapshot = SnapshotType<EditData>>\n implements SpreadsheetData<LayeredSnapshot<BaseSnapshot, EditSnapshot>> {\n\n /**\n * Creates a `LayeredSpreadsheetData` instance from two existing `SpreadsheetData` instances.\n * \n * All type parameters can be inferred from the constructor arguments.\n * \n * @param base - `SpreadsheetData` instance to use for the base layer\n * @param edit - `SpreadsheetData` instance to use for the edit layer\n */\n constructor(base: BaseData, edit: EditData) {\n this.#base = base;\n this.#edit = edit;\n }\n\n subscribe(onDataChange: () => void): () => void {\n const unsubscribeBase = this.#base.subscribe(onDataChange);\n const unsubscribeEdit = this.#edit.subscribe(onDataChange);\n return () => {\n unsubscribeBase();\n unsubscribeEdit();\n }\n }\n\n getSnapshot(): LayeredSnapshot<BaseSnapshot, EditSnapshot> {\n const baseSnapshot = this.#base.getSnapshot();\n const editSnapshot = this.#edit.getSnapshot();\n\n if (!this.#content || this.#content.base != baseSnapshot || this.#content.edit != editSnapshot) {\n this.#content = { base: baseSnapshot, edit: editSnapshot } ;\n }\n\n return asSnapshot(this.#content);\n }\n\n getRowCount(snapshot: LayeredSnapshot<BaseSnapshot, EditSnapshot>): number {\n const content = asContent(snapshot);\n return Math.max(this.#base.getRowCount(content.base), this.#edit.getRowCount(content.edit));\n }\n\n getRowItemOffsetMapping(snapshot: LayeredSnapshot<BaseSnapshot, EditSnapshot>): ItemOffsetMapping {\n return this.#base.getRowItemOffsetMapping(asContent(snapshot).base);\n }\n\n getColumnCount(snapshot: LayeredSnapshot<BaseSnapshot, EditSnapshot>): number {\n const content = asContent(snapshot);\n return Math.max(this.#base.getColumnCount(content.base), this.#edit.getColumnCount(content.edit));\n }\n\n getColumnItemOffsetMapping(snapshot: LayeredSnapshot<BaseSnapshot, EditSnapshot>): ItemOffsetMapping {\n return this.#base.getColumnItemOffsetMapping(asContent(snapshot).base);\n }\n\n getCellValue(snapshot: LayeredSnapshot<BaseSnapshot, EditSnapshot>, row: number, column: number): CellValue {\n const content = asContent(snapshot);\n const editValue = this.#edit.getCellValue(content.edit, row, column);\n if (editValue !== undefined)\n return editValue;\n\n return this.#base.getCellValue(content.base, row, column);\n }\n\n getCellFormat(snapshot: LayeredSnapshot<BaseSnapshot, EditSnapshot>, row: number, column: number): string | undefined {\n const content = asContent(snapshot);\n const editValue = this.#edit.getCellValue(content.edit, row, column);\n return (editValue === undefined) ? this.#base.getCellFormat(content.base, row, column) : this.#edit.getCellFormat(content.edit, row, column);\n }\n\n setCellValueAndFormat(row: number, column: number, value: CellValue, format: string | undefined): boolean {\n return this.#edit.setCellValueAndFormat(row, column, value, format);\n }\n\n #base: BaseData;\n #edit: EditData;\n #content: LayeredSnapshotContent<BaseSnapshot, EditSnapshot> | undefined;\n}\n"],"names":["asContent","asSnapshot"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@candidstartup/simple-spreadsheet-data",
|
|
3
|
+
"private": false,
|
|
4
|
+
"version": "0.8.0",
|
|
5
|
+
"description": "Reference implementations of SpreadsheetData",
|
|
6
|
+
"author": "Tim Wiegand <tim.wiegand@thecandidstartup.org>",
|
|
7
|
+
"license": "BSD-3-Clause",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/TheCandidStartup/infinisheet.git",
|
|
11
|
+
"directory": "packages/simple-spreadsheet-data"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/TheCandidStartup/infinisheet/issues"
|
|
15
|
+
},
|
|
16
|
+
"homepage": "https://github.com/TheCandidStartup/infinisheet/blob/main/packages/simple-spreadsheet-data/README.md",
|
|
17
|
+
"keywords": [
|
|
18
|
+
"typescript",
|
|
19
|
+
"spreadsheet",
|
|
20
|
+
"infinisheet"
|
|
21
|
+
],
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"type": "module",
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
29
|
+
"module": "./dist/index.js",
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"import": "./dist/index.js"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"dev": "vite",
|
|
39
|
+
"clean": "rimraf {dist,temp}",
|
|
40
|
+
"prebuild": "npm run clean",
|
|
41
|
+
"typecheck": "tsc -p tsconfig.json",
|
|
42
|
+
"build-tsc": "tsc -p tsconfig.build.json",
|
|
43
|
+
"build-rollup": "rollup -c ../rollup.config.mjs",
|
|
44
|
+
"build": "npm run build-rollup",
|
|
45
|
+
"lint": "eslint . --report-unused-disable-directives --max-warnings 0",
|
|
46
|
+
"devapi": "api-extractor run --local",
|
|
47
|
+
"prodapi": "api-extractor run",
|
|
48
|
+
"devbuild": "npm run build && npm run lint && npm run devapi",
|
|
49
|
+
"test": "vitest"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"@candidstartup/infinisheet-types": "^0.8.0"
|
|
53
|
+
},
|
|
54
|
+
"gitHead": "5f783ed7b6eef96cbc72fb8972ba04d6033f9ece"
|
|
55
|
+
}
|