@jarenjs/collection 0.87.0 → 0.89.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/docs/COLLECTION.md +3 -1
- package/package.json +4 -4
- package/src/interaction.js +11 -8
package/docs/COLLECTION.md
CHANGED
|
@@ -81,7 +81,9 @@ pending realization. `removedKeys` tells interaction to resolve a removal fallba
|
|
|
81
81
|
only bounded observations and JSON intent need enter app state.
|
|
82
82
|
|
|
83
83
|
Selection contains stable `keys`, or query/snapshot-scoped `mode:'all'` with
|
|
84
|
-
`exclusions`, or one range with stable `fromKey`/`toKey` endpoints
|
|
84
|
+
`exclusions`, or one range with stable `fromKey`/`toKey` endpoints and optional
|
|
85
|
+
exclusions. Toggling a range member excludes or restores it in both the UI and
|
|
86
|
+
complete output. Clearing selection resets its shift anchor. Shift ranges
|
|
85
87
|
survive asynchronous endpoint realization. `maxSelectedKeys` (4096) bounds explicit
|
|
86
88
|
keys/exclusions. `state`, `selected`, `toggle`, `selectAll`, `clear`, `restore`,
|
|
87
89
|
`cancelPending` and `update` expose the model. Range membership for rendered rows
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jarenjs/collection",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.89.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
7
7
|
"types": "./dist/types/index.d.ts",
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
"prepack": "npm run build:types"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@jarenjs/core": "^0.
|
|
56
|
-
"@jarenjs/view": "^0.
|
|
57
|
-
"@jarenjs/app": "^0.
|
|
55
|
+
"@jarenjs/core": "^0.89.0",
|
|
56
|
+
"@jarenjs/view": "^0.89.0",
|
|
57
|
+
"@jarenjs/app": "^0.89.0"
|
|
58
58
|
}
|
|
59
59
|
}
|
package/src/interaction.js
CHANGED
|
@@ -19,27 +19,30 @@ export function createCollectionInteraction(options) {
|
|
|
19
19
|
return { state: 'ready', ...focus };
|
|
20
20
|
}
|
|
21
21
|
function scoped() { return selection.query === config.query && selection.snapshot === config.snapshot; }
|
|
22
|
-
function
|
|
23
|
-
if (key == null) return false;
|
|
24
|
-
if (selection.mode === 'all' && scoped()) return !selection.exclusions.includes(key);
|
|
25
|
-
if (selection.keys.includes(key)) return true;
|
|
22
|
+
function covered(key) {
|
|
26
23
|
if (!scoped()) return false;
|
|
24
|
+
if (selection.mode === 'all') return true;
|
|
27
25
|
return selection.ranges.some((range) => {
|
|
28
26
|
if (key === range.fromKey || key === range.toKey) return true;
|
|
29
27
|
const index = config.indexOf?.(key), start = config.indexOf?.(range.fromKey), end = config.indexOf?.(range.toKey);
|
|
30
28
|
return index >= 0 && start >= 0 && end >= 0 && index >= Math.min(start, end) && index <= Math.max(start, end);
|
|
31
29
|
});
|
|
32
30
|
}
|
|
31
|
+
function selected(key) {
|
|
32
|
+
return key != null && (selection.keys.includes(key) || (covered(key) && !selection.exclusions.includes(key)));
|
|
33
|
+
}
|
|
33
34
|
function toggle(key) {
|
|
34
35
|
if (typeof key !== 'string') return {state:'error',reason:'invalid-selection'};
|
|
35
|
-
if (
|
|
36
|
+
if (covered(key)) {
|
|
36
37
|
const excluded = selection.exclusions.includes(key);
|
|
37
|
-
|
|
38
|
+
const keys = selection.keys.filter((item) => item !== key);
|
|
39
|
+
if (!excluded && keys.length + selection.exclusions.length >= config.maxSelectedKeys) return { state: 'budget-exhausted', reason: 'selection-credits' };
|
|
40
|
+
selection.keys = keys;
|
|
38
41
|
selection.exclusions = excluded ? selection.exclusions.filter((item) => item !== key) : [...selection.exclusions, key];
|
|
39
42
|
}
|
|
40
43
|
else {
|
|
41
44
|
const exists = selection.keys.includes(key);
|
|
42
|
-
if (!exists && selection.keys.length >= config.maxSelectedKeys) return { state: 'budget-exhausted', reason: 'selection-credits' };
|
|
45
|
+
if (!exists && selection.keys.length + selection.exclusions.length >= config.maxSelectedKeys) return { state: 'budget-exhausted', reason: 'selection-credits' };
|
|
43
46
|
selection.keys = exists ? selection.keys.filter((item) => item !== key) : [...selection.keys, key];
|
|
44
47
|
}
|
|
45
48
|
return { state: 'ready' };
|
|
@@ -67,7 +70,7 @@ export function createCollectionInteraction(options) {
|
|
|
67
70
|
},
|
|
68
71
|
toggle,
|
|
69
72
|
selectAll() { selection = { mode: 'all', keys: [], ranges: [], exclusions: [], query: config.query, snapshot: config.snapshot }; },
|
|
70
|
-
clear() { selection = { mode: 'keys', keys: [], ranges: [], exclusions: [], query: config.query, snapshot: config.snapshot }; },
|
|
73
|
+
clear() { rangeAnchor = null; selection = { mode: 'keys', keys: [], ranges: [], exclusions: [], query: config.query, snapshot: config.snapshot }; },
|
|
71
74
|
restore(intent) {
|
|
72
75
|
// Only JSON intent returns from navigation; loaded resources never travel here.
|
|
73
76
|
if (!intent || !isJsonValue(intent) || typeof intent.query !== 'string' || typeof intent.snapshot !== 'string' || !['keys', 'all'].includes(intent.mode) || !Array.isArray(intent.keys) || !Array.isArray(intent.exclusions)
|