@headless-tree/core 1.3.0 → 1.5.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/CHANGELOG.md +29 -0
- package/dist/index.d.mts +37 -10
- package/dist/index.d.ts +37 -10
- package/dist/index.js +190 -86
- package/dist/index.mjs +190 -86
- package/package.json +19 -3
- package/readme.md +6 -6
- package/src/core/create-tree.ts +45 -9
- package/src/features/async-data-loader/async-data-loader.spec.ts +1 -0
- package/src/features/async-data-loader/feature.ts +16 -20
- package/src/features/async-data-loader/types.ts +2 -1
- package/src/features/checkboxes/checkboxes.spec.ts +111 -122
- package/src/features/checkboxes/feature.ts +89 -40
- package/src/features/checkboxes/types.ts +16 -3
- package/src/features/drag-and-drop/feature.ts +63 -8
- package/src/features/drag-and-drop/types.ts +10 -0
- package/src/features/drag-and-drop/utils.ts +8 -6
- package/src/features/hotkeys-core/feature.ts +1 -0
- package/src/features/keyboard-drag-and-drop/feature.ts +3 -1
- package/src/features/main/types.ts +9 -0
- package/src/features/sync-data-loader/types.ts +7 -1
- package/src/mddocs-entry.ts +13 -0
- package/src/test-utils/test-tree-do.ts +6 -0
- package/src/test-utils/test-tree.ts +14 -3
- package/src/types/core.ts +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# @headless-tree/core
|
|
2
2
|
|
|
3
|
+
## 1.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- cbeaba6: all state updates (like setSelectedItems) will not propagate while the component is unmounted. This happened before for `tree.setState()` calls directly, but not individual state atoms like `setSelectedItems`. When calling `createTree()` directly (instead of `useTree()`), `tree.setMounted(true)` needs to be called once after mount. No changes are necessary when using the React-based `useTree()` integration. (#158)
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- 72e714b: all NPM deployments will now publish with provenance
|
|
12
|
+
- 6693986: fixed an issue where async data loaders cause calling `item.getItemData()` outside of the component calling `useTree()` to cause a React warning log (#158)
|
|
13
|
+
- 7a7424f: fixed incorrect exports definition in package.json for require/cjs imports (#161)
|
|
14
|
+
- 215ab4b: add a new symbol that can be used in hotkey configurations "metaorcontrol" that will trigger if either any windows control key or mac meta key is pressed (#141)
|
|
15
|
+
- 51b0dea: Added `isUnorderedDragTarget` as alternative to `isDragTarget` for easier detection of drag type
|
|
16
|
+
- cf845d7: Added new state variable `loadingCheckPropagationItems` to indicate if, in async trees with checkboxes and state propagation enabled, data loading operations are currently loading due to a checkbox propagation taking place
|
|
17
|
+
- 597faad: Checkbox propagation is now supported for trees with async data loaders!
|
|
18
|
+
- b0ee382: triggering a data refetch will now always set the loadingItemData/loadingItemChildrens state variable to the associated items if they where not apart of the cache before
|
|
19
|
+
|
|
20
|
+
## 1.4.0
|
|
21
|
+
|
|
22
|
+
### Minor Changes
|
|
23
|
+
|
|
24
|
+
- 7ef4864: added feature where closed items are auto-expanded briefly after dragging onto them. set config option `openOnDropDelay` to zero to disable.
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- 8d53b4f: fixed a bug where external changes to focused or selected items don't trigger a rerender (#150)
|
|
29
|
+
- 1cee368: fixed a bug where the drag line is not cleared after drop (#149)
|
|
30
|
+
- 1e833bb: drag-and-drop feature is no longer dependent on selection feature, and fill default to focused item if selection feature is missing (#143)
|
|
31
|
+
|
|
3
32
|
## 1.3.0
|
|
4
33
|
|
|
5
34
|
### Minor Changes
|
package/dist/index.d.mts
CHANGED
|
@@ -2,6 +2,7 @@ interface DndDataRef {
|
|
|
2
2
|
lastDragCode?: string;
|
|
3
3
|
lastAllowDrop?: boolean;
|
|
4
4
|
lastDragEnter?: number;
|
|
5
|
+
autoExpandTimeout?: any;
|
|
5
6
|
windowDragEndListener?: () => void;
|
|
6
7
|
}
|
|
7
8
|
interface DndState<T> {
|
|
@@ -65,6 +66,8 @@ type DragAndDropFeatureDef<T> = {
|
|
|
65
66
|
onDrop?: (items: ItemInstance<T>[], target: DragTarget<T>) => void | Promise<void>;
|
|
66
67
|
onDropForeignDragObject?: (dataTransfer: DataTransfer, target: DragTarget<T>) => void | Promise<void>;
|
|
67
68
|
onCompleteForeignDrop?: (items: ItemInstance<T>[]) => void;
|
|
69
|
+
/** When dragging for this many ms on a closed folder, the folder will automatically open. Set to zero to disable. */
|
|
70
|
+
openOnDropDelay?: number;
|
|
68
71
|
};
|
|
69
72
|
treeInstance: {
|
|
70
73
|
getDragTarget: () => DragTarget<T> | null;
|
|
@@ -72,7 +75,12 @@ type DragAndDropFeatureDef<T> = {
|
|
|
72
75
|
getDragLineStyle: (topOffset?: number, leftOffset?: number) => Record<string, any>;
|
|
73
76
|
};
|
|
74
77
|
itemInstance: {
|
|
78
|
+
/** Checks if the user is dragging in a way which makes this the new parent of the dragged items, either by dragging on top of
|
|
79
|
+
* this item, or by dragging inbetween children of this item. See @{isUnorderedDragTarget} if the latter is undesirable. */
|
|
75
80
|
isDragTarget: () => boolean;
|
|
81
|
+
/** As opposed to @{isDragTarget}, this will not be true if the target is inbetween children of this item. This returns only true
|
|
82
|
+
* if the user is dragging directly on top of this item. */
|
|
83
|
+
isUnorderedDragTarget: () => boolean;
|
|
76
84
|
isDragTargetAbove: () => boolean;
|
|
77
85
|
isDragTargetBelow: () => boolean;
|
|
78
86
|
isDraggingOver: () => boolean;
|
|
@@ -174,6 +182,10 @@ type MainFeatureDef<T = any> = {
|
|
|
174
182
|
};
|
|
175
183
|
getHotkeyPresets: () => HotkeysConfig<T>;
|
|
176
184
|
rebuildTree: () => void;
|
|
185
|
+
/** @deprecated Experimental feature, might get removed or changed in the future. */
|
|
186
|
+
scheduleRebuildTree: () => void;
|
|
187
|
+
/** @internal */
|
|
188
|
+
setMounted: (isMounted: boolean) => void;
|
|
177
189
|
};
|
|
178
190
|
itemInstance: {
|
|
179
191
|
registerElement: (element: HTMLElement | null) => void;
|
|
@@ -256,7 +268,12 @@ type SyncDataLoaderFeatureDef<T> = {
|
|
|
256
268
|
};
|
|
257
269
|
treeInstance: {
|
|
258
270
|
retrieveItemData: (itemId: string) => T;
|
|
259
|
-
|
|
271
|
+
/** Retrieve children Ids. If an async data loader is used, skipFetch is set to true, and children have not been retrieved
|
|
272
|
+
* yet for this item, this will initiate fetching the children, and return an empty array. Once the children have loaded,
|
|
273
|
+
* a rerender will be triggered.
|
|
274
|
+
* @param skipFetch - Defaults to false.
|
|
275
|
+
*/
|
|
276
|
+
retrieveChildrenIds: (itemId: string, skipFetch?: boolean) => string[];
|
|
260
277
|
};
|
|
261
278
|
itemInstance: {
|
|
262
279
|
isLoading: () => boolean;
|
|
@@ -304,7 +321,8 @@ type AsyncDataLoaderFeatureDef<T> = {
|
|
|
304
321
|
* @param optimistic If true, the item will not trigger a state update on `loadingItemChildrens`, and
|
|
305
322
|
* the tree will continue to display the old data until the new data has loaded. */
|
|
306
323
|
invalidateChildrenIds: (optimistic?: boolean) => Promise<void>;
|
|
307
|
-
|
|
324
|
+
/** Set to undefined to clear cache without triggering automatic refetch. Use @invalidateItemData to clear and triggering refetch. */
|
|
325
|
+
updateCachedData: (data: T | undefined) => void;
|
|
308
326
|
updateCachedChildrenIds: (childrenIds: string[]) => void;
|
|
309
327
|
isLoading: () => boolean;
|
|
310
328
|
};
|
|
@@ -440,9 +458,11 @@ declare enum CheckedState {
|
|
|
440
458
|
type CheckboxesFeatureDef<T> = {
|
|
441
459
|
state: {
|
|
442
460
|
checkedItems: string[];
|
|
461
|
+
loadingCheckPropagationItems: string[];
|
|
443
462
|
};
|
|
444
463
|
config: {
|
|
445
464
|
setCheckedItems?: SetStateFn<string[]>;
|
|
465
|
+
setLoadingCheckPropagationItems?: SetStateFn<string[]>;
|
|
446
466
|
canCheckFolders?: boolean;
|
|
447
467
|
propagateCheckedState?: boolean;
|
|
448
468
|
};
|
|
@@ -450,11 +470,18 @@ type CheckboxesFeatureDef<T> = {
|
|
|
450
470
|
setCheckedItems: (checkedItems: string[]) => void;
|
|
451
471
|
};
|
|
452
472
|
itemInstance: {
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
473
|
+
/** Will recursively load descendants if propagateCheckedState=true and async data loader is used. If not,
|
|
474
|
+
* this will return immediately. */
|
|
475
|
+
setChecked: () => Promise<void>;
|
|
476
|
+
/** Will recursively load descendants if propagateCheckedState=true and async data loader is used. If not,
|
|
477
|
+
* this will return immediately. */
|
|
478
|
+
setUnchecked: () => Promise<void>;
|
|
479
|
+
/** Will recursively load descendants if propagateCheckedState=true and async data loader is used. If not,
|
|
480
|
+
* this will return immediately. */
|
|
481
|
+
toggleCheckedState: () => Promise<void>;
|
|
456
482
|
getCheckedState: () => CheckedState;
|
|
457
483
|
getCheckboxProps: () => Record<string, any>;
|
|
484
|
+
isLoadingCheckPropagation: () => boolean;
|
|
458
485
|
};
|
|
459
486
|
hotkeys: never;
|
|
460
487
|
};
|
|
@@ -500,11 +527,11 @@ type HotkeyName = MergedFeatures<RegisteredFeatures<any>>["hotkeys"];
|
|
|
500
527
|
type HotkeysConfig<T> = Record<HotkeyName, HotkeyConfig<T>>;
|
|
501
528
|
type CustomHotkeysConfig<T> = Partial<Record<HotkeyName | `custom${string}`, Partial<HotkeyConfig<T>>>>;
|
|
502
529
|
type MayReturnNull<T extends (...x: any[]) => any> = (...args: Parameters<T>) => ReturnType<T> | null;
|
|
503
|
-
type ItemInstanceOpts<Key extends keyof ItemInstance<any>> = {
|
|
504
|
-
item: ItemInstance<
|
|
505
|
-
tree: TreeInstance<
|
|
530
|
+
type ItemInstanceOpts<T, Key extends keyof ItemInstance<any>> = {
|
|
531
|
+
item: ItemInstance<T>;
|
|
532
|
+
tree: TreeInstance<T>;
|
|
506
533
|
itemId: string;
|
|
507
|
-
prev?: MayReturnNull<ItemInstance<
|
|
534
|
+
prev?: MayReturnNull<ItemInstance<T>[Key]>;
|
|
508
535
|
};
|
|
509
536
|
type TreeInstanceOpts<Key extends keyof TreeInstance<any>> = {
|
|
510
537
|
tree: TreeInstance<any>;
|
|
@@ -521,7 +548,7 @@ type FeatureImplementation<T = any> = {
|
|
|
521
548
|
[key in keyof TreeInstance<T>]?: (opts: TreeInstanceOpts<key>, ...args: Parameters<TreeInstance<T>[key]>) => void;
|
|
522
549
|
};
|
|
523
550
|
itemInstance?: {
|
|
524
|
-
[key in keyof ItemInstance<T>]?: (opts: ItemInstanceOpts<key>, ...args: Parameters<ItemInstance<T>[key]>) => void;
|
|
551
|
+
[key in keyof ItemInstance<T>]?: (opts: ItemInstanceOpts<T, key>, ...args: Parameters<ItemInstance<T>[key]>) => void;
|
|
525
552
|
};
|
|
526
553
|
onTreeMount?: (instance: TreeInstance<T>, treeElement: HTMLElement) => void;
|
|
527
554
|
onTreeUnmount?: (instance: TreeInstance<T>, treeElement: HTMLElement) => void;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ interface DndDataRef {
|
|
|
2
2
|
lastDragCode?: string;
|
|
3
3
|
lastAllowDrop?: boolean;
|
|
4
4
|
lastDragEnter?: number;
|
|
5
|
+
autoExpandTimeout?: any;
|
|
5
6
|
windowDragEndListener?: () => void;
|
|
6
7
|
}
|
|
7
8
|
interface DndState<T> {
|
|
@@ -65,6 +66,8 @@ type DragAndDropFeatureDef<T> = {
|
|
|
65
66
|
onDrop?: (items: ItemInstance<T>[], target: DragTarget<T>) => void | Promise<void>;
|
|
66
67
|
onDropForeignDragObject?: (dataTransfer: DataTransfer, target: DragTarget<T>) => void | Promise<void>;
|
|
67
68
|
onCompleteForeignDrop?: (items: ItemInstance<T>[]) => void;
|
|
69
|
+
/** When dragging for this many ms on a closed folder, the folder will automatically open. Set to zero to disable. */
|
|
70
|
+
openOnDropDelay?: number;
|
|
68
71
|
};
|
|
69
72
|
treeInstance: {
|
|
70
73
|
getDragTarget: () => DragTarget<T> | null;
|
|
@@ -72,7 +75,12 @@ type DragAndDropFeatureDef<T> = {
|
|
|
72
75
|
getDragLineStyle: (topOffset?: number, leftOffset?: number) => Record<string, any>;
|
|
73
76
|
};
|
|
74
77
|
itemInstance: {
|
|
78
|
+
/** Checks if the user is dragging in a way which makes this the new parent of the dragged items, either by dragging on top of
|
|
79
|
+
* this item, or by dragging inbetween children of this item. See @{isUnorderedDragTarget} if the latter is undesirable. */
|
|
75
80
|
isDragTarget: () => boolean;
|
|
81
|
+
/** As opposed to @{isDragTarget}, this will not be true if the target is inbetween children of this item. This returns only true
|
|
82
|
+
* if the user is dragging directly on top of this item. */
|
|
83
|
+
isUnorderedDragTarget: () => boolean;
|
|
76
84
|
isDragTargetAbove: () => boolean;
|
|
77
85
|
isDragTargetBelow: () => boolean;
|
|
78
86
|
isDraggingOver: () => boolean;
|
|
@@ -174,6 +182,10 @@ type MainFeatureDef<T = any> = {
|
|
|
174
182
|
};
|
|
175
183
|
getHotkeyPresets: () => HotkeysConfig<T>;
|
|
176
184
|
rebuildTree: () => void;
|
|
185
|
+
/** @deprecated Experimental feature, might get removed or changed in the future. */
|
|
186
|
+
scheduleRebuildTree: () => void;
|
|
187
|
+
/** @internal */
|
|
188
|
+
setMounted: (isMounted: boolean) => void;
|
|
177
189
|
};
|
|
178
190
|
itemInstance: {
|
|
179
191
|
registerElement: (element: HTMLElement | null) => void;
|
|
@@ -256,7 +268,12 @@ type SyncDataLoaderFeatureDef<T> = {
|
|
|
256
268
|
};
|
|
257
269
|
treeInstance: {
|
|
258
270
|
retrieveItemData: (itemId: string) => T;
|
|
259
|
-
|
|
271
|
+
/** Retrieve children Ids. If an async data loader is used, skipFetch is set to true, and children have not been retrieved
|
|
272
|
+
* yet for this item, this will initiate fetching the children, and return an empty array. Once the children have loaded,
|
|
273
|
+
* a rerender will be triggered.
|
|
274
|
+
* @param skipFetch - Defaults to false.
|
|
275
|
+
*/
|
|
276
|
+
retrieveChildrenIds: (itemId: string, skipFetch?: boolean) => string[];
|
|
260
277
|
};
|
|
261
278
|
itemInstance: {
|
|
262
279
|
isLoading: () => boolean;
|
|
@@ -304,7 +321,8 @@ type AsyncDataLoaderFeatureDef<T> = {
|
|
|
304
321
|
* @param optimistic If true, the item will not trigger a state update on `loadingItemChildrens`, and
|
|
305
322
|
* the tree will continue to display the old data until the new data has loaded. */
|
|
306
323
|
invalidateChildrenIds: (optimistic?: boolean) => Promise<void>;
|
|
307
|
-
|
|
324
|
+
/** Set to undefined to clear cache without triggering automatic refetch. Use @invalidateItemData to clear and triggering refetch. */
|
|
325
|
+
updateCachedData: (data: T | undefined) => void;
|
|
308
326
|
updateCachedChildrenIds: (childrenIds: string[]) => void;
|
|
309
327
|
isLoading: () => boolean;
|
|
310
328
|
};
|
|
@@ -440,9 +458,11 @@ declare enum CheckedState {
|
|
|
440
458
|
type CheckboxesFeatureDef<T> = {
|
|
441
459
|
state: {
|
|
442
460
|
checkedItems: string[];
|
|
461
|
+
loadingCheckPropagationItems: string[];
|
|
443
462
|
};
|
|
444
463
|
config: {
|
|
445
464
|
setCheckedItems?: SetStateFn<string[]>;
|
|
465
|
+
setLoadingCheckPropagationItems?: SetStateFn<string[]>;
|
|
446
466
|
canCheckFolders?: boolean;
|
|
447
467
|
propagateCheckedState?: boolean;
|
|
448
468
|
};
|
|
@@ -450,11 +470,18 @@ type CheckboxesFeatureDef<T> = {
|
|
|
450
470
|
setCheckedItems: (checkedItems: string[]) => void;
|
|
451
471
|
};
|
|
452
472
|
itemInstance: {
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
473
|
+
/** Will recursively load descendants if propagateCheckedState=true and async data loader is used. If not,
|
|
474
|
+
* this will return immediately. */
|
|
475
|
+
setChecked: () => Promise<void>;
|
|
476
|
+
/** Will recursively load descendants if propagateCheckedState=true and async data loader is used. If not,
|
|
477
|
+
* this will return immediately. */
|
|
478
|
+
setUnchecked: () => Promise<void>;
|
|
479
|
+
/** Will recursively load descendants if propagateCheckedState=true and async data loader is used. If not,
|
|
480
|
+
* this will return immediately. */
|
|
481
|
+
toggleCheckedState: () => Promise<void>;
|
|
456
482
|
getCheckedState: () => CheckedState;
|
|
457
483
|
getCheckboxProps: () => Record<string, any>;
|
|
484
|
+
isLoadingCheckPropagation: () => boolean;
|
|
458
485
|
};
|
|
459
486
|
hotkeys: never;
|
|
460
487
|
};
|
|
@@ -500,11 +527,11 @@ type HotkeyName = MergedFeatures<RegisteredFeatures<any>>["hotkeys"];
|
|
|
500
527
|
type HotkeysConfig<T> = Record<HotkeyName, HotkeyConfig<T>>;
|
|
501
528
|
type CustomHotkeysConfig<T> = Partial<Record<HotkeyName | `custom${string}`, Partial<HotkeyConfig<T>>>>;
|
|
502
529
|
type MayReturnNull<T extends (...x: any[]) => any> = (...args: Parameters<T>) => ReturnType<T> | null;
|
|
503
|
-
type ItemInstanceOpts<Key extends keyof ItemInstance<any>> = {
|
|
504
|
-
item: ItemInstance<
|
|
505
|
-
tree: TreeInstance<
|
|
530
|
+
type ItemInstanceOpts<T, Key extends keyof ItemInstance<any>> = {
|
|
531
|
+
item: ItemInstance<T>;
|
|
532
|
+
tree: TreeInstance<T>;
|
|
506
533
|
itemId: string;
|
|
507
|
-
prev?: MayReturnNull<ItemInstance<
|
|
534
|
+
prev?: MayReturnNull<ItemInstance<T>[Key]>;
|
|
508
535
|
};
|
|
509
536
|
type TreeInstanceOpts<Key extends keyof TreeInstance<any>> = {
|
|
510
537
|
tree: TreeInstance<any>;
|
|
@@ -521,7 +548,7 @@ type FeatureImplementation<T = any> = {
|
|
|
521
548
|
[key in keyof TreeInstance<T>]?: (opts: TreeInstanceOpts<key>, ...args: Parameters<TreeInstance<T>[key]>) => void;
|
|
522
549
|
};
|
|
523
550
|
itemInstance?: {
|
|
524
|
-
[key in keyof ItemInstance<T>]?: (opts: ItemInstanceOpts<key>, ...args: Parameters<ItemInstance<T>[key]>) => void;
|
|
551
|
+
[key in keyof ItemInstance<T>]?: (opts: ItemInstanceOpts<T, key>, ...args: Parameters<ItemInstance<T>[key]>) => void;
|
|
525
552
|
};
|
|
526
553
|
onTreeMount?: (instance: TreeInstance<T>, treeElement: HTMLElement) => void;
|
|
527
554
|
onTreeUnmount?: (instance: TreeInstance<T>, treeElement: HTMLElement) => void;
|