@headless-tree/core 0.0.0-20250511190858 → 0.0.0-20250511194715
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/CHANGELOG.md +2 -1
- package/lib/cjs/features/expand-all/feature.js +2 -2
- package/lib/cjs/features/hotkeys-core/feature.js +27 -13
- package/lib/cjs/features/keyboard-drag-and-drop/feature.js +1 -1
- package/lib/cjs/features/selection/feature.js +1 -1
- package/lib/esm/features/expand-all/feature.js +2 -2
- package/lib/esm/features/hotkeys-core/feature.js +27 -13
- package/lib/esm/features/keyboard-drag-and-drop/feature.js +1 -1
- package/lib/esm/features/selection/feature.js +1 -1
- package/package.json +1 -1
- package/src/features/expand-all/feature.ts +2 -2
- package/src/features/hotkeys-core/feature.ts +33 -15
- package/src/features/keyboard-drag-and-drop/feature.ts +1 -1
- package/src/features/selection/feature.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# @headless-tree/core
|
|
2
2
|
|
|
3
|
-
## 0.0.0-
|
|
3
|
+
## 0.0.0-20250511194715
|
|
4
4
|
|
|
5
5
|
### Minor Changes
|
|
6
6
|
|
|
7
7
|
- 64d8e2a: add getChildrenWithData method to data loader to support fetching all children of an item at once
|
|
8
|
+
- 35260e3: fixed hotkey issues where releasing modifier keys (like shift) before normal keys can cause issues with subsequent keydown events
|
|
8
9
|
|
|
9
10
|
### Patch Changes
|
|
10
11
|
|
|
@@ -51,7 +51,7 @@ exports.expandAllFeature = {
|
|
|
51
51
|
handler: (_, tree) => __awaiter(void 0, void 0, void 0, function* () {
|
|
52
52
|
const cancelToken = { current: false };
|
|
53
53
|
const cancelHandler = (e) => {
|
|
54
|
-
if (e.
|
|
54
|
+
if (e.code === "Escape") {
|
|
55
55
|
cancelToken.current = true;
|
|
56
56
|
}
|
|
57
57
|
};
|
|
@@ -61,7 +61,7 @@ exports.expandAllFeature = {
|
|
|
61
61
|
}),
|
|
62
62
|
},
|
|
63
63
|
collapseSelected: {
|
|
64
|
-
hotkey: "Control+Shift
|
|
64
|
+
hotkey: "Control+Shift+Minus",
|
|
65
65
|
handler: (_, tree) => {
|
|
66
66
|
tree.getSelectedItems().forEach((item) => item.collapseAll());
|
|
67
67
|
},
|
|
@@ -2,16 +2,31 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.hotkeysCoreFeature = void 0;
|
|
4
4
|
const specialKeys = {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
// TODO:breaking deprecate auto-lowercase
|
|
6
|
+
letter: /^Key[A-Z]$/,
|
|
7
|
+
letterornumber: /^(Key[A-Z]|Digit[0-9])$/,
|
|
8
|
+
plus: /^(NumpadAdd|Plus)$/,
|
|
9
|
+
minus: /^(NumpadSubtract|Minus)$/,
|
|
10
|
+
control: /^(ControlLeft|ControlRight)$/,
|
|
11
|
+
shift: /^(ShiftLeft|ShiftRight)$/,
|
|
9
12
|
};
|
|
10
13
|
const testHotkeyMatch = (pressedKeys, tree, hotkey) => {
|
|
11
|
-
const supposedKeys = hotkey.hotkey.toLowerCase().split("+");
|
|
12
|
-
const doKeysMatch = supposedKeys.every((key) =>
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
const supposedKeys = hotkey.hotkey.toLowerCase().split("+"); // TODO:breaking deprecate auto-lowercase
|
|
15
|
+
const doKeysMatch = supposedKeys.every((key) => {
|
|
16
|
+
if (key in specialKeys) {
|
|
17
|
+
return [...pressedKeys].some((pressedKey) => specialKeys[key].test(pressedKey));
|
|
18
|
+
}
|
|
19
|
+
const pressedKeysLowerCase = [...pressedKeys] // TODO:breaking deprecate auto-lowercase
|
|
20
|
+
.map((k) => k.toLowerCase());
|
|
21
|
+
if (pressedKeysLowerCase.includes(key.toLowerCase())) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
if (pressedKeysLowerCase.includes(`key${key.toLowerCase()}`)) {
|
|
25
|
+
// TODO:breaking deprecate e.key character matching
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
|
+
});
|
|
15
30
|
const isEnabled = !hotkey.isEnabled || hotkey.isEnabled(tree);
|
|
16
31
|
const equalCounts = pressedKeys.size === supposedKeys.length;
|
|
17
32
|
return doKeysMatch && isEnabled && equalCounts;
|
|
@@ -31,15 +46,14 @@ exports.hotkeysCoreFeature = {
|
|
|
31
46
|
if (e.target instanceof HTMLInputElement && ignoreHotkeysOnInputs) {
|
|
32
47
|
return;
|
|
33
48
|
}
|
|
34
|
-
const key = e.key.toLowerCase();
|
|
35
49
|
(_a = (_b = data.current).pressedKeys) !== null && _a !== void 0 ? _a : (_b.pressedKeys = new Set());
|
|
36
|
-
const newMatch = !data.current.pressedKeys.has(
|
|
37
|
-
data.current.pressedKeys.add(
|
|
50
|
+
const newMatch = !data.current.pressedKeys.has(e.code);
|
|
51
|
+
data.current.pressedKeys.add(e.code);
|
|
38
52
|
const hotkeyName = findHotkeyMatch(data.current.pressedKeys, tree, tree.getHotkeyPresets(), hotkeys);
|
|
39
53
|
if (e.target instanceof HTMLInputElement) {
|
|
40
54
|
// JS respects composite keydowns while input elements are focused, and
|
|
41
55
|
// doesnt send the associated keyup events with the same key name
|
|
42
|
-
data.current.pressedKeys.delete(
|
|
56
|
+
data.current.pressedKeys.delete(e.code);
|
|
43
57
|
}
|
|
44
58
|
if (!hotkeyName)
|
|
45
59
|
return;
|
|
@@ -60,7 +74,7 @@ exports.hotkeysCoreFeature = {
|
|
|
60
74
|
var _a;
|
|
61
75
|
var _b;
|
|
62
76
|
(_a = (_b = data.current).pressedKeys) !== null && _a !== void 0 ? _a : (_b.pressedKeys = new Set());
|
|
63
|
-
data.current.pressedKeys.delete(e.
|
|
77
|
+
data.current.pressedKeys.delete(e.code);
|
|
64
78
|
};
|
|
65
79
|
const reset = () => {
|
|
66
80
|
data.current.pressedKeys = new Set();
|
|
@@ -48,7 +48,7 @@ export const expandAllFeature = {
|
|
|
48
48
|
handler: (_, tree) => __awaiter(void 0, void 0, void 0, function* () {
|
|
49
49
|
const cancelToken = { current: false };
|
|
50
50
|
const cancelHandler = (e) => {
|
|
51
|
-
if (e.
|
|
51
|
+
if (e.code === "Escape") {
|
|
52
52
|
cancelToken.current = true;
|
|
53
53
|
}
|
|
54
54
|
};
|
|
@@ -58,7 +58,7 @@ export const expandAllFeature = {
|
|
|
58
58
|
}),
|
|
59
59
|
},
|
|
60
60
|
collapseSelected: {
|
|
61
|
-
hotkey: "Control+Shift
|
|
61
|
+
hotkey: "Control+Shift+Minus",
|
|
62
62
|
handler: (_, tree) => {
|
|
63
63
|
tree.getSelectedItems().forEach((item) => item.collapseAll());
|
|
64
64
|
},
|
|
@@ -1,14 +1,29 @@
|
|
|
1
1
|
const specialKeys = {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
// TODO:breaking deprecate auto-lowercase
|
|
3
|
+
letter: /^Key[A-Z]$/,
|
|
4
|
+
letterornumber: /^(Key[A-Z]|Digit[0-9])$/,
|
|
5
|
+
plus: /^(NumpadAdd|Plus)$/,
|
|
6
|
+
minus: /^(NumpadSubtract|Minus)$/,
|
|
7
|
+
control: /^(ControlLeft|ControlRight)$/,
|
|
8
|
+
shift: /^(ShiftLeft|ShiftRight)$/,
|
|
6
9
|
};
|
|
7
10
|
const testHotkeyMatch = (pressedKeys, tree, hotkey) => {
|
|
8
|
-
const supposedKeys = hotkey.hotkey.toLowerCase().split("+");
|
|
9
|
-
const doKeysMatch = supposedKeys.every((key) =>
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
const supposedKeys = hotkey.hotkey.toLowerCase().split("+"); // TODO:breaking deprecate auto-lowercase
|
|
12
|
+
const doKeysMatch = supposedKeys.every((key) => {
|
|
13
|
+
if (key in specialKeys) {
|
|
14
|
+
return [...pressedKeys].some((pressedKey) => specialKeys[key].test(pressedKey));
|
|
15
|
+
}
|
|
16
|
+
const pressedKeysLowerCase = [...pressedKeys] // TODO:breaking deprecate auto-lowercase
|
|
17
|
+
.map((k) => k.toLowerCase());
|
|
18
|
+
if (pressedKeysLowerCase.includes(key.toLowerCase())) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
if (pressedKeysLowerCase.includes(`key${key.toLowerCase()}`)) {
|
|
22
|
+
// TODO:breaking deprecate e.key character matching
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
});
|
|
12
27
|
const isEnabled = !hotkey.isEnabled || hotkey.isEnabled(tree);
|
|
13
28
|
const equalCounts = pressedKeys.size === supposedKeys.length;
|
|
14
29
|
return doKeysMatch && isEnabled && equalCounts;
|
|
@@ -28,15 +43,14 @@ export const hotkeysCoreFeature = {
|
|
|
28
43
|
if (e.target instanceof HTMLInputElement && ignoreHotkeysOnInputs) {
|
|
29
44
|
return;
|
|
30
45
|
}
|
|
31
|
-
const key = e.key.toLowerCase();
|
|
32
46
|
(_a = (_b = data.current).pressedKeys) !== null && _a !== void 0 ? _a : (_b.pressedKeys = new Set());
|
|
33
|
-
const newMatch = !data.current.pressedKeys.has(
|
|
34
|
-
data.current.pressedKeys.add(
|
|
47
|
+
const newMatch = !data.current.pressedKeys.has(e.code);
|
|
48
|
+
data.current.pressedKeys.add(e.code);
|
|
35
49
|
const hotkeyName = findHotkeyMatch(data.current.pressedKeys, tree, tree.getHotkeyPresets(), hotkeys);
|
|
36
50
|
if (e.target instanceof HTMLInputElement) {
|
|
37
51
|
// JS respects composite keydowns while input elements are focused, and
|
|
38
52
|
// doesnt send the associated keyup events with the same key name
|
|
39
|
-
data.current.pressedKeys.delete(
|
|
53
|
+
data.current.pressedKeys.delete(e.code);
|
|
40
54
|
}
|
|
41
55
|
if (!hotkeyName)
|
|
42
56
|
return;
|
|
@@ -57,7 +71,7 @@ export const hotkeysCoreFeature = {
|
|
|
57
71
|
var _a;
|
|
58
72
|
var _b;
|
|
59
73
|
(_a = (_b = data.current).pressedKeys) !== null && _a !== void 0 ? _a : (_b.pressedKeys = new Set());
|
|
60
|
-
data.current.pressedKeys.delete(e.
|
|
74
|
+
data.current.pressedKeys.delete(e.code);
|
|
61
75
|
};
|
|
62
76
|
const reset = () => {
|
|
63
77
|
data.current.pressedKeys = new Set();
|
package/package.json
CHANGED
|
@@ -50,7 +50,7 @@ export const expandAllFeature: FeatureImplementation = {
|
|
|
50
50
|
handler: async (_, tree) => {
|
|
51
51
|
const cancelToken = { current: false };
|
|
52
52
|
const cancelHandler = (e: KeyboardEvent) => {
|
|
53
|
-
if (e.
|
|
53
|
+
if (e.code === "Escape") {
|
|
54
54
|
cancelToken.current = true;
|
|
55
55
|
}
|
|
56
56
|
};
|
|
@@ -63,7 +63,7 @@ export const expandAllFeature: FeatureImplementation = {
|
|
|
63
63
|
},
|
|
64
64
|
|
|
65
65
|
collapseSelected: {
|
|
66
|
-
hotkey: "Control+Shift
|
|
66
|
+
hotkey: "Control+Shift+Minus",
|
|
67
67
|
handler: (_, tree) => {
|
|
68
68
|
tree.getSelectedItems().forEach((item) => item.collapseAll());
|
|
69
69
|
},
|
|
@@ -6,10 +6,13 @@ import {
|
|
|
6
6
|
import { HotkeyConfig, HotkeysCoreDataRef } from "./types";
|
|
7
7
|
|
|
8
8
|
const specialKeys: Record<string, RegExp> = {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
// TODO:breaking deprecate auto-lowercase
|
|
10
|
+
letter: /^Key[A-Z]$/,
|
|
11
|
+
letterornumber: /^(Key[A-Z]|Digit[0-9])$/,
|
|
12
|
+
plus: /^(NumpadAdd|Plus)$/,
|
|
13
|
+
minus: /^(NumpadSubtract|Minus)$/,
|
|
14
|
+
control: /^(ControlLeft|ControlRight)$/,
|
|
15
|
+
shift: /^(ShiftLeft|ShiftRight)$/,
|
|
13
16
|
};
|
|
14
17
|
|
|
15
18
|
const testHotkeyMatch = (
|
|
@@ -17,12 +20,28 @@ const testHotkeyMatch = (
|
|
|
17
20
|
tree: TreeInstance<any>,
|
|
18
21
|
hotkey: HotkeyConfig<any>,
|
|
19
22
|
) => {
|
|
20
|
-
const supposedKeys = hotkey.hotkey.toLowerCase().split("+");
|
|
21
|
-
const doKeysMatch = supposedKeys.every((key) =>
|
|
22
|
-
key in specialKeys
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
const supposedKeys = hotkey.hotkey.toLowerCase().split("+"); // TODO:breaking deprecate auto-lowercase
|
|
24
|
+
const doKeysMatch = supposedKeys.every((key) => {
|
|
25
|
+
if (key in specialKeys) {
|
|
26
|
+
return [...pressedKeys].some((pressedKey) =>
|
|
27
|
+
specialKeys[key].test(pressedKey),
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const pressedKeysLowerCase = [...pressedKeys] // TODO:breaking deprecate auto-lowercase
|
|
32
|
+
.map((k) => k.toLowerCase());
|
|
33
|
+
|
|
34
|
+
if (pressedKeysLowerCase.includes(key.toLowerCase())) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (pressedKeysLowerCase.includes(`key${key.toLowerCase()}`)) {
|
|
39
|
+
// TODO:breaking deprecate e.key character matching
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return false;
|
|
44
|
+
});
|
|
26
45
|
const isEnabled = !hotkey.isEnabled || hotkey.isEnabled(tree);
|
|
27
46
|
const equalCounts = pressedKeys.size === supposedKeys.length;
|
|
28
47
|
return doKeysMatch && isEnabled && equalCounts;
|
|
@@ -50,10 +69,9 @@ export const hotkeysCoreFeature: FeatureImplementation = {
|
|
|
50
69
|
return;
|
|
51
70
|
}
|
|
52
71
|
|
|
53
|
-
const key = e.key.toLowerCase();
|
|
54
72
|
data.current.pressedKeys ??= new Set();
|
|
55
|
-
const newMatch = !data.current.pressedKeys.has(
|
|
56
|
-
data.current.pressedKeys.add(
|
|
73
|
+
const newMatch = !data.current.pressedKeys.has(e.code);
|
|
74
|
+
data.current.pressedKeys.add(e.code);
|
|
57
75
|
|
|
58
76
|
const hotkeyName = findHotkeyMatch(
|
|
59
77
|
data.current.pressedKeys,
|
|
@@ -65,7 +83,7 @@ export const hotkeysCoreFeature: FeatureImplementation = {
|
|
|
65
83
|
if (e.target instanceof HTMLInputElement) {
|
|
66
84
|
// JS respects composite keydowns while input elements are focused, and
|
|
67
85
|
// doesnt send the associated keyup events with the same key name
|
|
68
|
-
data.current.pressedKeys.delete(
|
|
86
|
+
data.current.pressedKeys.delete(e.code);
|
|
69
87
|
}
|
|
70
88
|
|
|
71
89
|
if (!hotkeyName) return;
|
|
@@ -90,7 +108,7 @@ export const hotkeysCoreFeature: FeatureImplementation = {
|
|
|
90
108
|
|
|
91
109
|
const keyup = (e: KeyboardEvent) => {
|
|
92
110
|
data.current.pressedKeys ??= new Set();
|
|
93
|
-
data.current.pressedKeys.delete(e.
|
|
111
|
+
data.current.pressedKeys.delete(e.code);
|
|
94
112
|
};
|
|
95
113
|
|
|
96
114
|
const reset = () => {
|
|
@@ -187,7 +187,7 @@ export const keyboardDragAndDropFeature: FeatureImplementation = {
|
|
|
187
187
|
|
|
188
188
|
hotkeys: {
|
|
189
189
|
startDrag: {
|
|
190
|
-
hotkey: "Control+Shift+
|
|
190
|
+
hotkey: "Control+Shift+KeyD",
|
|
191
191
|
preventDefault: true,
|
|
192
192
|
isEnabled: (tree) => !tree.getState().dnd,
|
|
193
193
|
handler: (_, tree) => {
|
|
@@ -144,7 +144,7 @@ export const selectionFeature: FeatureImplementation = {
|
|
|
144
144
|
},
|
|
145
145
|
},
|
|
146
146
|
selectAll: {
|
|
147
|
-
hotkey: "Control+
|
|
147
|
+
hotkey: "Control+KeyA",
|
|
148
148
|
preventDefault: true,
|
|
149
149
|
handler: (e, tree) => {
|
|
150
150
|
tree.setSelectedItems(tree.getItems().map((item) => item.getId()));
|