js-spread-grid 0.2.7 → 1.1.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/dist/index.js +1 -1
- package/package.json +2 -1
- package/src/core/cursor.js +20 -0
- package/src/core/error.js +30 -0
- package/src/core/input.js +42 -0
- package/src/core/render.js +25 -159
- package/src/core/scroll.js +219 -0
- package/src/core/state.js +176 -71
- package/src/core/style.js +26 -0
- package/src/core/tooltip.js +33 -0
- package/src/core-utils/rect.js +14 -12
- package/src/core-utils/rect.test.js +7 -7
- package/src/core-utils/stringifyId.js +4 -2
- package/src/index.d.ts +6 -0
- package/src/index.js +200 -158
- package/src/state-utils/getActive.js +7 -6
- package/src/state-utils/getBoundingClientSize.js +14 -0
- package/src/state-utils/getCellEditType.js +5 -3
- package/src/state-utils/getCellPlacement.js +8 -6
- package/src/state-utils/getCellSection.js +9 -7
- package/src/state-utils/getClientSize.js +12 -0
- package/src/state-utils/getClipboardData.js +6 -4
- package/src/state-utils/getColumnIndex.js +3 -1
- package/src/state-utils/getCombinedCells.js +3 -1
- package/src/state-utils/getContextFormatting.js +4 -2
- package/src/state-utils/getCursorState.js +11 -0
- package/src/state-utils/getDataFormatting.js +11 -9
- package/src/state-utils/getDynamicRowHeight.js +10 -0
- package/src/state-utils/getEditableCells.js +7 -5
- package/src/state-utils/getEditedCellsAndFilters.js +5 -3
- package/src/state-utils/getEdition.js +3 -1
- package/src/state-utils/getFilterFormatting.js +4 -2
- package/src/state-utils/getFiltered.js +39 -19
- package/src/state-utils/getFilteringRules.js +3 -1
- package/src/state-utils/getFixedSize.js +91 -6
- package/src/state-utils/getFormatResolver.js +6 -4
- package/src/state-utils/getFormattingRules.js +3 -1
- package/src/state-utils/getGenerated.js +8 -6
- package/src/state-utils/getHighlightedCells.js +9 -7
- package/src/state-utils/getHoveredCell.js +13 -14
- package/src/state-utils/getInputFormatting.js +4 -2
- package/src/state-utils/getInputPlacement.js +8 -6
- package/src/state-utils/getInputState.js +10 -0
- package/src/state-utils/getInternalPosition.js +12 -16
- package/src/state-utils/getIsTextValid.js +3 -1
- package/src/state-utils/getKeys.js +12 -3
- package/src/state-utils/getLookup.js +4 -2
- package/src/state-utils/getMeasureFormatting.js +4 -2
- package/src/state-utils/getMeasured.js +36 -20
- package/src/state-utils/getMousePosition.js +3 -1
- package/src/state-utils/getNewSortBy.js +14 -9
- package/src/state-utils/getOrder.js +4 -2
- package/src/state-utils/getOrdered.js +62 -25
- package/src/state-utils/getPinned.js +17 -3
- package/src/state-utils/getPinning.js +43 -0
- package/src/state-utils/getPlaced.js +17 -14
- package/src/state-utils/getReducedCells.js +4 -2
- package/src/state-utils/getReducedFormatting.js +11 -9
- package/src/state-utils/getRenderFormatting.js +26 -16
- package/src/state-utils/getRenderState.js +14 -0
- package/src/state-utils/getReordered.js +13 -11
- package/src/state-utils/getResizable.js +24 -26
- package/src/state-utils/getResolved.js +115 -52
- package/src/state-utils/getResolvedFilters.js +4 -2
- package/src/state-utils/getResolvedScrollSpeed.js +37 -0
- package/src/state-utils/getResolvedSortBy.js +4 -2
- package/src/state-utils/getRowIndex.js +3 -1
- package/src/state-utils/getScrollOffset.js +12 -0
- package/src/state-utils/getScrollRect.js +12 -18
- package/src/state-utils/getScrollState.js +19 -0
- package/src/state-utils/getScrollTarget.js +136 -0
- package/src/state-utils/getSections.js +6 -4
- package/src/state-utils/getSelection.js +3 -1
- package/src/state-utils/getSortOrderFormatting.js +11 -0
- package/src/state-utils/getSorted.js +44 -30
- package/src/state-utils/getSortingFormatting.js +4 -2
- package/src/state-utils/getSortingRules.js +3 -1
- package/src/state-utils/getStatic.js +251 -0
- package/src/state-utils/getStatic.test.js +455 -0
- package/src/state-utils/getStyleState.js +11 -0
- package/src/state-utils/getToggledValue.js +6 -4
- package/src/state-utils/getTooltip.js +6 -4
- package/src/state-utils/getTooltipPlacement.js +4 -2
- package/src/state-utils/getTooltipState.js +10 -0
- package/src/state-utils/getTotalSize.js +47 -6
- package/src/state-utils/getUnfolded.js +23 -21
- package/src/state-utils/getWithAssumedId.js +5 -3
- package/src/types/Edition.js +14 -12
- package/src/types/FilteringRules.js +11 -9
- package/src/types/FormatResolver.js +8 -6
- package/src/types/FormattingRules.js +28 -19
- package/src/types/FormattingRules.test.js +6 -5
- package/src/types/RulesLookup.js +12 -10
- package/src/types/Selection.js +8 -6
- package/src/types/SortingRules.js +37 -21
- package/src/types/TextResolver.js +4 -2
- package/src/typings.js +351 -35
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/** @import * as Types from "../typings.js"; */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @param {Types.ScrollSpeedStep[]} speed
|
|
5
|
+
* @param {number} distance
|
|
6
|
+
* @returns {Types.ScrollSpeed}
|
|
7
|
+
*/
|
|
8
|
+
function getSpeedValue(speed, distance) {
|
|
9
|
+
const absDistance = Math.abs(distance);
|
|
10
|
+
for (const entry of speed) {
|
|
11
|
+
if (absDistance <= entry.maxDistance)
|
|
12
|
+
return entry.scrollSpeed;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return 'auto';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {Types.Context} context
|
|
21
|
+
*/
|
|
22
|
+
function cancelScrollAnimation(context) {
|
|
23
|
+
const element = context.element;
|
|
24
|
+
|
|
25
|
+
if (context.scrollAnimationId) {
|
|
26
|
+
cancelAnimationFrame(context.scrollAnimationId);
|
|
27
|
+
context.scrollAnimationId = null;
|
|
28
|
+
element.scrollTop = element.scrollTop;
|
|
29
|
+
element.scrollLeft = element.scrollLeft;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param {number} diff
|
|
35
|
+
* @param {number} speed
|
|
36
|
+
* @param {number} dt
|
|
37
|
+
* @param {number} fractionalProgress
|
|
38
|
+
* @returns {number}
|
|
39
|
+
*/
|
|
40
|
+
function getStep(diff, speed, dt, fractionalProgress) {
|
|
41
|
+
return diff > 0
|
|
42
|
+
? Math.min(diff, speed * dt + fractionalProgress)
|
|
43
|
+
: Math.max(diff, -speed * dt + fractionalProgress);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @param {number} target
|
|
48
|
+
* @param {number} clientSize
|
|
49
|
+
* @param {number} scrollSize
|
|
50
|
+
* @returns {number}
|
|
51
|
+
*/
|
|
52
|
+
function clipTarget(target, clientSize, scrollSize) {
|
|
53
|
+
return Math.max(0, Math.min(target, scrollSize - clientSize - 1));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @param {'top' | 'left'} axis
|
|
58
|
+
* @param {number} dt
|
|
59
|
+
* @param {number} currentPosition
|
|
60
|
+
* @param {number} target
|
|
61
|
+
* @param {number} clientSize
|
|
62
|
+
* @param {number} scrollSize
|
|
63
|
+
* @param {number} previousTarget
|
|
64
|
+
* @param {number} previousOffset
|
|
65
|
+
* @param {Types.ScrollSpeedStep[]} speedSteps
|
|
66
|
+
* @param {number} fractionalProgress
|
|
67
|
+
* @param {boolean} snap
|
|
68
|
+
* @returns {ScrollToOptions & {behavior?: 'auto'} | null}
|
|
69
|
+
*/
|
|
70
|
+
function getOptions(axis, dt, currentPosition, target, clientSize, scrollSize, previousTarget, previousOffset, speedSteps, fractionalProgress, snap) {
|
|
71
|
+
if (target === null)
|
|
72
|
+
return null;
|
|
73
|
+
|
|
74
|
+
const diff = target - currentPosition;
|
|
75
|
+
|
|
76
|
+
if (Math.abs(diff) < 1)
|
|
77
|
+
return null;
|
|
78
|
+
|
|
79
|
+
if (diff < 0 && currentPosition === 0)
|
|
80
|
+
return null;
|
|
81
|
+
|
|
82
|
+
if (diff > 0 && currentPosition >= scrollSize - clientSize - 1)
|
|
83
|
+
return null;
|
|
84
|
+
|
|
85
|
+
const speed = getSpeedValue(speedSteps, diff);
|
|
86
|
+
|
|
87
|
+
if (snap || speed === 'auto')
|
|
88
|
+
return { [axis]: target, behavior: 'auto' };
|
|
89
|
+
|
|
90
|
+
if (typeof speed === 'number')
|
|
91
|
+
return { [axis]: currentPosition + getStep(diff, speed, dt, fractionalProgress), behavior: 'auto' };
|
|
92
|
+
|
|
93
|
+
if (previousTarget !== target || previousOffset === currentPosition)
|
|
94
|
+
return { [axis]: target };
|
|
95
|
+
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @param {Types.Context} context
|
|
101
|
+
* @param {number} dt
|
|
102
|
+
* @param {ScrollToOptions} previousTarget
|
|
103
|
+
* @param {Types.ScrollOffset} previousOffset
|
|
104
|
+
* @param {number} fractionalProgress
|
|
105
|
+
* @param {boolean} snap
|
|
106
|
+
* @returns {ScrollToOptions & {behavior?: 'auto'}}
|
|
107
|
+
*/
|
|
108
|
+
function getVerticalOptions(context, dt, previousTarget, previousOffset, fractionalProgress, snap) {
|
|
109
|
+
const element = context.element;
|
|
110
|
+
const scrollState = context.state.scrollState;
|
|
111
|
+
const target = scrollState.verticalTarget;
|
|
112
|
+
const currentPosition = element.scrollTop;
|
|
113
|
+
const clientSize = element.clientHeight;
|
|
114
|
+
const scrollSize = element.scrollHeight;
|
|
115
|
+
|
|
116
|
+
return getOptions('top', dt, currentPosition, target, clientSize, scrollSize, previousTarget?.top, previousOffset.top, scrollState.verticalSpeed, fractionalProgress, snap);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @param {Types.Context} context
|
|
121
|
+
* @param {number} dt
|
|
122
|
+
* @param {ScrollToOptions} previousTarget
|
|
123
|
+
* @param {Types.ScrollOffset} previousOffset
|
|
124
|
+
* @param {number} fractionalProgress
|
|
125
|
+
* @param {boolean} snap
|
|
126
|
+
* @returns {ScrollToOptions & {behavior?: 'auto'}}
|
|
127
|
+
*/
|
|
128
|
+
function getHorizontalOptions(context, dt, previousTarget, previousOffset, fractionalProgress, snap) {
|
|
129
|
+
const element = context.element;
|
|
130
|
+
const scrollState = context.state.scrollState;
|
|
131
|
+
const target = scrollState.horizontalTarget;
|
|
132
|
+
const currentPosition = element.scrollLeft;
|
|
133
|
+
const clientSize = element.clientWidth;
|
|
134
|
+
const scrollSize = element.scrollWidth;
|
|
135
|
+
|
|
136
|
+
return getOptions('left', dt, currentPosition, target, clientSize, scrollSize, previousTarget?.left, previousOffset.left, scrollState.horizontalSpeed, fractionalProgress, snap);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* @param {Types.Context} context
|
|
141
|
+
*/
|
|
142
|
+
export default function scroll(context) {
|
|
143
|
+
const element = context.element;
|
|
144
|
+
const scrollState = context.state.scrollState;
|
|
145
|
+
const targetV = scrollState.verticalTarget;
|
|
146
|
+
const targetH = scrollState.horizontalTarget;
|
|
147
|
+
|
|
148
|
+
if (targetV === null && targetH === null || !element.isConnected) {
|
|
149
|
+
cancelScrollAnimation(context);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (context.scrollAnimationId) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
let lastTime = performance.now();
|
|
158
|
+
let previousOptionsH = /** @type {ScrollToOptions|null} */ (null);
|
|
159
|
+
let previousOptionsV = /** @type {ScrollToOptions|null} */ (null);
|
|
160
|
+
let previousOffset = { top: element.scrollTop, left: element.scrollLeft };
|
|
161
|
+
let fractionalVerticalProgress = 0;
|
|
162
|
+
let fractionalHorizontalProgress = 0;
|
|
163
|
+
|
|
164
|
+
/** @param {number} now */
|
|
165
|
+
const animate = (now) => {
|
|
166
|
+
const targetV = context.state.scrollState.verticalTarget;
|
|
167
|
+
const targetH = context.state.scrollState.horizontalTarget;
|
|
168
|
+
|
|
169
|
+
if (targetV === null && targetH === null || !element.isConnected) {
|
|
170
|
+
cancelScrollAnimation(context);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (targetV === null)
|
|
175
|
+
previousOptionsV = null;
|
|
176
|
+
if (targetH === null)
|
|
177
|
+
previousOptionsH = null;
|
|
178
|
+
|
|
179
|
+
const dt = (now - lastTime) / 1000;
|
|
180
|
+
lastTime = now;
|
|
181
|
+
|
|
182
|
+
const snap = context.lastScrollClientSizeVersion !== context.state.scrollState.clientSizeVersion;
|
|
183
|
+
context.lastScrollClientSizeVersion = context.state.scrollState.clientSizeVersion;
|
|
184
|
+
|
|
185
|
+
const newOptionsV = getVerticalOptions(context, dt, previousOptionsV, previousOffset, fractionalVerticalProgress, snap);
|
|
186
|
+
const newOptionsH = getHorizontalOptions(context, dt, previousOptionsH, previousOffset, fractionalHorizontalProgress, snap);
|
|
187
|
+
const newOptions = {
|
|
188
|
+
...previousOptionsV,
|
|
189
|
+
...previousOptionsH,
|
|
190
|
+
behavior: /** @type {Types.ScrollBehavior} */('smooth'),
|
|
191
|
+
...newOptionsV,
|
|
192
|
+
...newOptionsH
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
if (newOptionsV || newOptionsH) {
|
|
196
|
+
element.scrollTo(newOptions);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (newOptionsV && newOptions.behavior === 'auto') {
|
|
200
|
+
fractionalVerticalProgress = newOptions.top - element.scrollTop;
|
|
201
|
+
} else {
|
|
202
|
+
fractionalVerticalProgress = 0;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (newOptionsH && newOptions.behavior === 'auto') {
|
|
206
|
+
fractionalHorizontalProgress = newOptions.left - element.scrollLeft;
|
|
207
|
+
} else {
|
|
208
|
+
fractionalHorizontalProgress = 0;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
previousOptionsV = newOptionsV || previousOptionsV;
|
|
212
|
+
previousOptionsH = newOptionsH || previousOptionsH;
|
|
213
|
+
previousOffset = { top: element.scrollTop, left: element.scrollLeft };
|
|
214
|
+
|
|
215
|
+
context.scrollAnimationId = requestAnimationFrame(animate);
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
animate(lastTime);
|
|
219
|
+
}
|
package/src/core/state.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** @import * as Types from "../typings.js"; */
|
|
2
|
+
|
|
1
3
|
import getEditableCells from "../state-utils/getEditableCells.js";
|
|
2
4
|
import getDataFormatting from "../state-utils/getDataFormatting.js";
|
|
3
5
|
import getInputFormatting from "../state-utils/getInputFormatting.js";
|
|
@@ -28,6 +30,7 @@ import getMeasureFormatting from "../state-utils/getMeasureFormatting.js";
|
|
|
28
30
|
import { getMeasuredColumns, getMeasuredRows } from "../state-utils/getMeasured.js";
|
|
29
31
|
import getKeys from "../state-utils/getKeys.js";
|
|
30
32
|
import getSortingFormatting from "../state-utils/getSortingFormatting.js";
|
|
33
|
+
import getSortOrderFormatting from "../state-utils/getSortOrderFormatting.js";
|
|
31
34
|
import getSortingRules from "../state-utils/getSortingRules.js";
|
|
32
35
|
import { getSortedColumns, getSortedRows } from "../state-utils/getSorted.js";
|
|
33
36
|
import { getResizableColumn, getResizableRow } from "../state-utils/getResizable.js";
|
|
@@ -38,13 +41,107 @@ import getContextFormatting from "../state-utils/getContextFormatting.js";
|
|
|
38
41
|
import getTooltip from "../state-utils/getTooltip.js";
|
|
39
42
|
import getTooltipPlacement from "../state-utils/getTooltipPlacement.js";
|
|
40
43
|
import getOrder from "../state-utils/getOrder.js";
|
|
41
|
-
import
|
|
44
|
+
import { getOrderedColumns, getOrderedRows } from "../state-utils/getOrdered.js";
|
|
45
|
+
import { getStaticColumns, getStaticRows } from "../state-utils/getStatic.js";
|
|
46
|
+
import getPinning from "../state-utils/getPinning.js";
|
|
47
|
+
import getClientSize from "../state-utils/getClientSize.js";
|
|
48
|
+
import getScrollOffset from "../state-utils/getScrollOffset.js";
|
|
49
|
+
import getInternalPosition from "../state-utils/getInternalPosition.js";
|
|
50
|
+
import stringifyId from "../core-utils/stringifyId.js";
|
|
51
|
+
import { getHorizontalScrollTarget, getVerticalScrollTarget } from "../state-utils/getScrollTarget.js";
|
|
52
|
+
import getResolvedScrollSpeed from "../state-utils/getResolvedScrollSpeed.js";
|
|
53
|
+
import getScrollState from "../state-utils/getScrollState.js";
|
|
54
|
+
import getStyleState from "../state-utils/getStyleState.js";
|
|
55
|
+
import getRenderState from "../state-utils/getRenderState.js";
|
|
56
|
+
import getInputState from "../state-utils/getInputState.js";
|
|
57
|
+
import getCursorState from "../state-utils/getCursorState.js";
|
|
58
|
+
import getTooltipState from "../state-utils/getTooltipState.js";
|
|
59
|
+
import getDynamicRowHeight from "../state-utils/getDynamicRowHeight.js";
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @param {Types.Position} a
|
|
63
|
+
* @param {Types.Position} b
|
|
64
|
+
* @returns {boolean}
|
|
65
|
+
*/
|
|
66
|
+
function comparePositions(a, b) {
|
|
67
|
+
if (!a && !b)
|
|
68
|
+
return true;
|
|
69
|
+
if (!a || !b)
|
|
70
|
+
return false;
|
|
71
|
+
return a.x === b.x && a.y === b.y;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @param {Types.ClientSize} a
|
|
76
|
+
* @param {Types.ClientSize} b
|
|
77
|
+
* @returns {boolean}
|
|
78
|
+
*/
|
|
79
|
+
function compareClientSizes(a, b) {
|
|
80
|
+
if (!a && !b)
|
|
81
|
+
return true;
|
|
82
|
+
if (!a || !b)
|
|
83
|
+
return false;
|
|
84
|
+
return a.width === b.width && a.height === b.height;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @param {Types.ScrollOffset} a
|
|
89
|
+
* @param {Types.ScrollOffset} b
|
|
90
|
+
* @returns {boolean}
|
|
91
|
+
*/
|
|
92
|
+
function compareScrollOffsets(a, b) {
|
|
93
|
+
if (!a && !b)
|
|
94
|
+
return true;
|
|
95
|
+
if (!a || !b)
|
|
96
|
+
return false;
|
|
97
|
+
return a.left === b.left && a.top === b.top;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* @param {Types.Rect} a
|
|
102
|
+
* @param {Types.Rect} b
|
|
103
|
+
* @returns {boolean}
|
|
104
|
+
*/
|
|
105
|
+
function compareRects(a, b) {
|
|
106
|
+
if (!a && !b)
|
|
107
|
+
return true;
|
|
108
|
+
if (!a || !b)
|
|
109
|
+
return false;
|
|
110
|
+
return a.left === b.left && a.top === b.top && a.width === b.width && a.height === b.height;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @param {Types.CellId} a
|
|
115
|
+
* @param {Types.CellId} b
|
|
116
|
+
* @returns {boolean}
|
|
117
|
+
*/
|
|
118
|
+
function compareCellIds(a, b) {
|
|
119
|
+
if (!a && !b)
|
|
120
|
+
return true;
|
|
121
|
+
if (!a || !b)
|
|
122
|
+
return false;
|
|
123
|
+
return stringifyId(a.rowId) === stringifyId(b.rowId) && stringifyId(a.columnId) === stringifyId(b.columnId);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @param {Types.StyleState} a
|
|
128
|
+
* @param {Types.StyleState} b
|
|
129
|
+
* @returns {boolean}
|
|
130
|
+
*/
|
|
131
|
+
function compareStyleStates(a, b) {
|
|
132
|
+
if (!a && !b)
|
|
133
|
+
return true;
|
|
134
|
+
if (!a || !b)
|
|
135
|
+
return false;
|
|
136
|
+
return a.scrollbarWidth === b.scrollbarWidth;
|
|
137
|
+
}
|
|
138
|
+
|
|
42
139
|
|
|
43
140
|
// TODO: write some test to check if the cache is working properly
|
|
44
141
|
/**
|
|
45
|
-
* @param {Context} context
|
|
142
|
+
* @param {Types.Context} context
|
|
46
143
|
*/
|
|
47
|
-
function
|
|
144
|
+
export default function updateState(context) {
|
|
48
145
|
// console.count('updateState');
|
|
49
146
|
|
|
50
147
|
const options = { ...context.localOptions, ...context.externalOptions };
|
|
@@ -57,53 +154,46 @@ function updateStateInternal(context) {
|
|
|
57
154
|
|
|
58
155
|
// TODO: Move to utils
|
|
59
156
|
/**
|
|
60
|
-
* @template {keyof Memory} K
|
|
157
|
+
* @template {keyof Types.Memory} K
|
|
61
158
|
* @template {Array<*>} U
|
|
62
159
|
* @param {K} key
|
|
63
|
-
* @param {(...args: U) => Memory[K]['value']} func
|
|
160
|
+
* @param {(...args: U) => Types.Memory[K]['value']} func
|
|
64
161
|
* @param {U} dependencies
|
|
65
|
-
* @
|
|
162
|
+
* @param {(a: Types.Memory[K]['value'], b: Types.Memory[K]['value']) => boolean} [comparator]
|
|
163
|
+
* @returns {Types.Memory[K]['value']}
|
|
66
164
|
*/
|
|
67
|
-
function cache(key, func, dependencies) {
|
|
165
|
+
function cache(key, func, dependencies, comparator = (a, b) => a === b) {
|
|
68
166
|
const previousDependencies = memory[key] && memory[key].dependencies;
|
|
69
167
|
if (!previousDependencies || dependencies.some((dependency, index) => dependency !== previousDependencies[index])) {
|
|
70
|
-
memory[key]
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
* @param {Memory[K]['value']} value
|
|
80
|
-
* @returns {Memory[K]['value']}
|
|
81
|
-
*/
|
|
82
|
-
function cache_value_deep(key, value) {
|
|
83
|
-
const previousValue = memory[key] && memory[key].value;
|
|
84
|
-
if (JSON.stringify(previousValue) !== JSON.stringify(value)) {
|
|
85
|
-
memory[key] = /** @type {Memory[K]} */ ({ value });
|
|
86
|
-
stateChanges.push(key);
|
|
168
|
+
const previousValue = memory[key] && memory[key].value;
|
|
169
|
+
const previousVersion = memory[key] && memory[key].version || 0;
|
|
170
|
+
const newValue = func(...dependencies);
|
|
171
|
+
if (!comparator(previousValue, newValue)) {
|
|
172
|
+
memory[key] = /** @type {Types.Memory[K]} */ ({ value: newValue, dependencies, version: previousVersion + 1 });
|
|
173
|
+
stateChanges.push(key);
|
|
174
|
+
} else {
|
|
175
|
+
memory[key] = /** @type {Types.Memory[K]} */ ({ value: previousValue, dependencies, version: previousVersion });
|
|
176
|
+
}
|
|
87
177
|
}
|
|
88
178
|
return memory[key].value;
|
|
89
179
|
}
|
|
90
180
|
|
|
91
181
|
/**
|
|
92
|
-
* @template {keyof Memory} K
|
|
182
|
+
* @template {keyof Types.Memory} K
|
|
93
183
|
* @param {K} key
|
|
94
|
-
* @param {Memory[K]['value']} value
|
|
95
|
-
* @param {(value: Memory[K]['value']) => void} callback
|
|
184
|
+
* @param {Types.Memory[K]['value']} value
|
|
185
|
+
* @param {(value: Types.Memory[K]['value']) => void} callback
|
|
96
186
|
*/
|
|
97
187
|
function invoke_change_callback(key, value, callback) {
|
|
98
188
|
const previousValue = memory[key] && memory[key].value;
|
|
99
189
|
if (previousValue !== value) {
|
|
100
|
-
memory[key] = /** @type {Memory[K]} */ ({ value });
|
|
190
|
+
memory[key] = /** @type {Types.Memory[K]} */ ({ value });
|
|
101
191
|
callback(value);
|
|
102
192
|
}
|
|
103
193
|
}
|
|
104
194
|
|
|
105
|
-
const devicePixelRatio =
|
|
106
|
-
const borderWidth = options.borderWidth / devicePixelRatio;
|
|
195
|
+
const devicePixelRatio = /** @type {number} */ cache('devicePixelRatio', v => v, [window.devicePixelRatio]);
|
|
196
|
+
const borderWidth = /** @type {number} */ cache('borderWidth', v => v, [options.borderWidth / devicePixelRatio]);
|
|
107
197
|
const data = options.data;
|
|
108
198
|
const text = context.input.value;
|
|
109
199
|
const sortBy = cache('sortBy', getResolvedSortBy, [options.sortBy]);
|
|
@@ -114,22 +204,25 @@ function updateStateInternal(context) {
|
|
|
114
204
|
const edition = cache('edition', getEdition, [editedCellsAndFilters]);
|
|
115
205
|
const invokedColumns = cache('invokedColumns', getGeneratedColumns, [options.columns, data]);
|
|
116
206
|
const invokedRows = cache('invokedRows', getGeneratedRows, [options.rows, data]);
|
|
207
|
+
const mousePosition = cache('mousePosition', v => v, [context.mousePosition], comparePositions);
|
|
117
208
|
// TODO: throw on duplicate ids
|
|
118
209
|
// TODO: throw on duplicate row/column filter ids
|
|
119
210
|
|
|
120
211
|
// Resolving
|
|
121
212
|
const unfoldedColumns = cache('unfoldedColumns', getUnfoldedColumns, [invokedColumns, data]);
|
|
122
213
|
const unfoldedRows = cache('unfoldedRows', getUnfoldedRows, [invokedRows, data]);
|
|
123
|
-
const
|
|
124
|
-
const
|
|
214
|
+
const dynamicColumnWidth = 150;
|
|
215
|
+
const dynamicRowHeight = cache('dynamicRowHeight', getDynamicRowHeight, [textResolver]);
|
|
216
|
+
const unfilteredColumns = cache('unfilteredColumns', getResolvedColumns, [unfoldedColumns, options.columnWidths, borderWidth, devicePixelRatio, dynamicColumnWidth]);
|
|
217
|
+
const unfilteredRows = cache('unfilteredRows', getResolvedRows, [unfoldedRows, options.rowHeights, borderWidth, devicePixelRatio, dynamicRowHeight]);
|
|
125
218
|
const unfilteredColumnKeys = cache('unfilteredColumnKeys', getKeys, [unfilteredColumns]);
|
|
126
219
|
const unfilteredRowKeys = cache('unfilteredRowKeys', getKeys, [unfilteredRows]);
|
|
127
220
|
|
|
128
221
|
// Ordering
|
|
129
222
|
const columnsOrder = cache('columnsOrder', getOrder, [options.columnsOrder]);
|
|
130
223
|
const rowsOrder = cache('rowsOrder', getOrder, [options.rowsOrder]);
|
|
131
|
-
const orderedColumns = cache('orderedColumns',
|
|
132
|
-
const orderedRows = cache('orderedRows',
|
|
224
|
+
const orderedColumns = cache('orderedColumns', getOrderedColumns, [unfilteredColumns, columnsOrder]);
|
|
225
|
+
const orderedRows = cache('orderedRows', getOrderedRows, [unfilteredRows, rowsOrder]);
|
|
133
226
|
|
|
134
227
|
// Filtering
|
|
135
228
|
const filterFormatting = cache('filterFormatting', getFilterFormatting, [dataFormatting]);
|
|
@@ -145,9 +238,10 @@ function updateStateInternal(context) {
|
|
|
145
238
|
const sortedColumns = cache('sortedColumns', getSortedColumns, [sortBy, sortingRules, sortingFormattingRules, data, filteredRows, filteredColumns, edition]);
|
|
146
239
|
const sortedRows = cache('sortedRows', getSortedRows, [sortBy, sortingRules, sortingFormattingRules, data, filteredRows, filteredColumns, edition]);
|
|
147
240
|
|
|
148
|
-
// Shaping
|
|
241
|
+
// Shaping finalization
|
|
149
242
|
const shapedColumns = sortedColumns;
|
|
150
243
|
const shapedRows = sortedRows;
|
|
244
|
+
const pinning = cache('pinning', getPinning, [shapedColumns, shapedRows, options.pinnedTop, options.pinnedBottom, options.pinnedLeft, options.pinnedRight]);
|
|
151
245
|
|
|
152
246
|
// Measuring
|
|
153
247
|
const measureFormatting = cache('measureFormatting', getMeasureFormatting, [dataFormatting]);
|
|
@@ -155,24 +249,39 @@ function updateStateInternal(context) {
|
|
|
155
249
|
const measureFormatResolver = cache('measureFormatResolver', getFormatResolver, [measureFormattingRules, data, shapedRows, shapedColumns, edition]);
|
|
156
250
|
const columnWidthCache = context.columnWidthCache;
|
|
157
251
|
const rowHeightCache = context.rowHeightCache;
|
|
158
|
-
const measuredColumns = cache('measuredColumns', getMeasuredColumns, [shapedColumns, shapedRows, textResolver, measureFormatResolver, columnWidthCache, unfilteredColumnKeys]);
|
|
159
|
-
const measuredRows = cache('measuredRows', getMeasuredRows, [shapedColumns, shapedRows, textResolver, measureFormatResolver, rowHeightCache, unfilteredRowKeys]);
|
|
252
|
+
const measuredColumns = cache('measuredColumns', getMeasuredColumns, [shapedColumns, shapedRows, textResolver, measureFormatResolver, columnWidthCache, unfilteredColumnKeys, devicePixelRatio]);
|
|
253
|
+
const measuredRows = cache('measuredRows', getMeasuredRows, [shapedColumns, shapedRows, textResolver, measureFormatResolver, rowHeightCache, unfilteredRowKeys, devicePixelRatio]);
|
|
254
|
+
|
|
255
|
+
// Scrolling
|
|
256
|
+
const fixedSize = cache('fixedSize', getFixedSize, [measuredColumns, measuredRows, pinning, borderWidth]);
|
|
257
|
+
const totalSize = cache('totalSize', getTotalSize, [measuredColumns, measuredRows, borderWidth]);
|
|
258
|
+
const clientSize = cache('clientSize', v => v, [getClientSize(element)], compareClientSizes);
|
|
259
|
+
const boundingClientSize = cache('boundingClientSize', v => v, [getClientSize(element)], compareClientSizes);
|
|
260
|
+
const scrollOffset = cache('scrollOffset', v => v, [getScrollOffset(element)], compareScrollOffsets);
|
|
261
|
+
const internalMousePosition = cache('internalMousePosition', getInternalPosition, [mousePosition, clientSize, scrollOffset, fixedSize, totalSize], comparePositions);
|
|
262
|
+
const scrollRect = cache('scrollRect', getScrollRect, [previousState?.renderState?.scrollRect, scrollOffset, totalSize, fixedSize, boundingClientSize], compareRects);
|
|
263
|
+
const horizontalScrollTarget = cache('horizontalScrollTarget', getHorizontalScrollTarget, [options.horizontalScrollTarget, options.disableScrollOnHover, measuredColumns, pinning, internalMousePosition, fixedSize, totalSize, clientSize, borderWidth]);
|
|
264
|
+
const verticalScrollTarget = cache('verticalScrollTarget', getVerticalScrollTarget, [options.verticalScrollTarget, options.disableScrollOnHover, measuredRows, pinning, internalMousePosition, fixedSize, totalSize, clientSize, borderWidth]);
|
|
265
|
+
const horizontalScrollSpeed = cache('horizontalScrollSpeed', getResolvedScrollSpeed, [options.horizontalScrollSpeed]);
|
|
266
|
+
const verticalScrollSpeed = cache('verticalScrollSpeed', getResolvedScrollSpeed, [options.verticalScrollSpeed]);
|
|
267
|
+
|
|
268
|
+
// Dynamic allocation
|
|
269
|
+
const staticColumns = cache('staticColumns', getStaticColumns, [data, measuredColumns, pinning, scrollRect, fixedSize, borderWidth]);
|
|
270
|
+
const staticRows = cache('staticRows', getStaticRows, [data, measuredRows, pinning, scrollRect, fixedSize, borderWidth]);
|
|
160
271
|
|
|
161
272
|
// Placement
|
|
162
|
-
const columns = cache('columns', getPlacedColumns, [
|
|
163
|
-
const rows = cache('rows', getPlacedRows, [
|
|
273
|
+
const columns = cache('columns', getPlacedColumns, [staticColumns, pinning, borderWidth]);
|
|
274
|
+
const rows = cache('rows', getPlacedRows, [staticRows, pinning, borderWidth]);
|
|
164
275
|
const columnLookup = cache('columnLookup', getLookup, [columns]);
|
|
165
276
|
const rowLookup = cache('rowLookup', getLookup, [rows]);
|
|
166
277
|
const focusedCell = options.focusedCell;
|
|
167
278
|
const sections = cache('sections', getSections, [columns, rows]);
|
|
168
279
|
const selectedCells = options.selectedCells;
|
|
169
|
-
const
|
|
170
|
-
const totalSize = cache('totalSize', getTotalSize, [columns, rows]);
|
|
171
|
-
const hoveredCell = cache_value_deep('hoveredCell', getHoveredCell(element, context.mousePosition, rows, columns, fixedSize, totalSize));
|
|
280
|
+
const hoveredCell = cache('hoveredCell', getHoveredCell, [internalMousePosition, rows, columns, fixedSize, totalSize], compareCellIds);
|
|
172
281
|
const isReordering = context.isReordering;
|
|
173
282
|
// TODO: this does not seem to be cached properly (see onStateChange)
|
|
174
|
-
const resizableColumn = context.resizingColumn || cache('resizableColumn', getResizableColumn, [columns, columnLookup, rowLookup, hoveredCell,
|
|
175
|
-
const resizableRow = context.resizingRow || cache('resizableRow', getResizableRow, [rows, columnLookup, rowLookup, hoveredCell,
|
|
283
|
+
const resizableColumn = context.resizingColumn || cache('resizableColumn', getResizableColumn, [columns, columnLookup, rowLookup, hoveredCell, internalMousePosition, isReordering]);
|
|
284
|
+
const resizableRow = context.resizingRow || cache('resizableRow', getResizableRow, [rows, columnLookup, rowLookup, hoveredCell, internalMousePosition, isReordering]);
|
|
176
285
|
const isMouseDown = context.isMouseDown;
|
|
177
286
|
const canHighlight = !resizableColumn && !resizableRow && !context.didReorder;
|
|
178
287
|
const highlightedCells = cache('highlightedCells', getHighlightedCells, [isMouseDown, canHighlight, focusedCell, hoveredCell, columns, rows, columnLookup, rowLookup]);
|
|
@@ -182,6 +291,9 @@ function updateStateInternal(context) {
|
|
|
182
291
|
const renderFormatting = cache('renderFormatting', getRenderFormatting, [dataFormatting, hoveredCell, focusedCell, selection, highlight, edition, sortBy, resizableColumn, resizableRow, options.borderWidth, isReordering, context.reorderedColumn, context.reorderedRow]);
|
|
183
292
|
const renderFormattingRules = cache('renderFormattingRules', getFormattingRules, [renderFormatting]);
|
|
184
293
|
const renderFormatResolver = cache('renderFormatResolver', getFormatResolver, [renderFormattingRules, data, rows, columns, edition]);
|
|
294
|
+
const sortOrderFormatting = cache('sortOrderFormatting', getSortOrderFormatting, [dataFormatting]);
|
|
295
|
+
const sortOrderFormattingRules = cache('sortOrderFormattingRules', getFormattingRules, [sortOrderFormatting]);
|
|
296
|
+
const sortOrderFormatResolver = cache('sortOrderFormatResolver', getFormatResolver, [sortOrderFormattingRules, data, rows, columns, edition]);
|
|
185
297
|
const inputFormatting = cache('inputFormatting', getInputFormatting, [dataFormatting]);
|
|
186
298
|
const inputFormattingRules = cache('inputFormattingRules', getFormattingRules, [inputFormatting]);
|
|
187
299
|
const inputFormatResolver = cache('inputFormatResolver', getFormatResolver, [inputFormattingRules, data, rows, columns, edition]);
|
|
@@ -192,8 +304,17 @@ function updateStateInternal(context) {
|
|
|
192
304
|
const contextFormattingRules = cache('contextFormattingRules', getFormattingRules, [contextFormatting]);
|
|
193
305
|
const contextFormatResolver = cache('contextFormatResolver', getFormatResolver, [contextFormattingRules, data, rows, columns, edition]);
|
|
194
306
|
const tooltip = cache('tooltip', getTooltip, [hoveredCell, contextFormatResolver, columnLookup, rowLookup]);
|
|
195
|
-
const tooltipPlacement = cache('tooltipPlacement', getTooltipPlacement, [tooltip,
|
|
196
|
-
|
|
307
|
+
const tooltipPlacement = cache('tooltipPlacement', getTooltipPlacement, [tooltip, mousePosition]);
|
|
308
|
+
|
|
309
|
+
// Sub-states
|
|
310
|
+
const renderState = cache('renderState', getRenderState, [sections, devicePixelRatio, scrollRect, renderFormatResolver, borderWidth, textResolver]);
|
|
311
|
+
const inputState = cache('inputState', getInputState, [inputPlacement, isTextValid]);
|
|
312
|
+
const cursorState = cache('cursorState', getCursorState, [resizableColumn, resizableRow, context.isReordering]);
|
|
313
|
+
const tooltipState = cache('tooltipState', getTooltipState, [tooltip, tooltipPlacement]);
|
|
314
|
+
const scrollState = cache('scrollState', getScrollState, [verticalScrollTarget, horizontalScrollTarget, verticalScrollSpeed, horizontalScrollSpeed, memory.clientSize.version]);
|
|
315
|
+
|
|
316
|
+
// Style
|
|
317
|
+
const styleState = cache('styleState', getStyleState, [options.style], compareStyleStates);
|
|
197
318
|
|
|
198
319
|
// callbacks
|
|
199
320
|
const activeColumns = cache('activeColumns', getActiveColumns, [columns]);
|
|
@@ -207,42 +328,26 @@ function updateStateInternal(context) {
|
|
|
207
328
|
|
|
208
329
|
context.state = {
|
|
209
330
|
options,
|
|
210
|
-
devicePixelRatio,
|
|
211
|
-
borderWidth,
|
|
212
331
|
edition,
|
|
213
332
|
columns,
|
|
214
333
|
rows,
|
|
215
|
-
sections,
|
|
216
334
|
hoveredCell,
|
|
217
335
|
focusedCell,
|
|
218
|
-
renderFormatResolver,
|
|
219
336
|
inputFormatResolver,
|
|
220
337
|
fixedSize,
|
|
221
338
|
totalSize,
|
|
222
|
-
|
|
223
|
-
|
|
339
|
+
clientSize,
|
|
340
|
+
scrollOffset,
|
|
224
341
|
highlightedCells,
|
|
225
|
-
inputPlacement,
|
|
226
342
|
columnLookup,
|
|
227
343
|
rowLookup,
|
|
228
344
|
text,
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
345
|
+
scrollState,
|
|
346
|
+
styleState,
|
|
347
|
+
sortOrderFormatResolver,
|
|
348
|
+
renderState,
|
|
349
|
+
inputState,
|
|
350
|
+
cursorState,
|
|
351
|
+
tooltipState,
|
|
234
352
|
};
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* @param {Context} context
|
|
239
|
-
*/
|
|
240
|
-
export default function updateState(context) {
|
|
241
|
-
if (!context.error) {
|
|
242
|
-
try {
|
|
243
|
-
updateStateInternal(context);
|
|
244
|
-
} catch (error) {
|
|
245
|
-
context.error = error;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
353
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** @import * as Types from "../typings.js"; */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @param {Types.Context} context
|
|
5
|
+
*/
|
|
6
|
+
export default function style(context) {
|
|
7
|
+
// console.log('style', context.state.styleState);
|
|
8
|
+
|
|
9
|
+
const state = context.state.styleState;
|
|
10
|
+
const element = context.element;
|
|
11
|
+
const style = `
|
|
12
|
+
max-width: 100vw;
|
|
13
|
+
max-height: 100vh;
|
|
14
|
+
overflow: auto;
|
|
15
|
+
display: grid;
|
|
16
|
+
position: relative;
|
|
17
|
+
grid-template-columns: fit-content(0) fit-content(0) fit-content(0);
|
|
18
|
+
grid-template-rows: fit-content(0) fit-content(0) fit-content(0);
|
|
19
|
+
outline: none;
|
|
20
|
+
user-select: none;
|
|
21
|
+
touch-action: manipulation;
|
|
22
|
+
scrollbar-width: ${state.scrollbarWidth};
|
|
23
|
+
`
|
|
24
|
+
|
|
25
|
+
element.setAttribute('style', style);
|
|
26
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** @import * as Types from "../typings.js"; */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @param {Types.Context} context
|
|
5
|
+
*/
|
|
6
|
+
export default function showTooltip(context) {
|
|
7
|
+
const element = context.element;
|
|
8
|
+
const tooltipElement = context.tooltip;
|
|
9
|
+
const tooltipState = context.state.tooltipState;
|
|
10
|
+
const content = tooltipState.tooltip;
|
|
11
|
+
const placement = tooltipState.tooltipPlacement;
|
|
12
|
+
|
|
13
|
+
if (!placement) {
|
|
14
|
+
if (tooltipElement.parentElement)
|
|
15
|
+
tooltipElement.parentElement.removeChild(tooltipElement);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (tooltipElement.innerHTML !== content) {
|
|
20
|
+
tooltipElement.innerHTML = content;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
tooltipElement.style.left = `calc(anchor(left) + ${placement.left}px)`;
|
|
24
|
+
tooltipElement.style.top = `calc(anchor(top) + ${placement.top}px)`;
|
|
25
|
+
|
|
26
|
+
if (!tooltipElement.parentElement) {
|
|
27
|
+
element.appendChild(tooltipElement);
|
|
28
|
+
// @ts-ignore
|
|
29
|
+
tooltipElement.showPopover({
|
|
30
|
+
source: element,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|