@react-hive/honey-utils 3.21.0 → 3.23.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.
@@ -2118,7 +2118,7 @@ __webpack_require__.r(__webpack_exports__);
2118
2118
  * - Temporary UI keys (e.g. React lists, animations)
2119
2119
  * - In-memory references scoped to a single runtime
2120
2120
  *
2121
- * @returns An ephemeral identifier string.
2121
+ * @returns An ephemeral string identifier.
2122
2122
  */
2123
2123
  const generateEphemeralId = () => {
2124
2124
  const timestampPart = Math.floor(performance.now() * 1000).toString(36);
@@ -2793,6 +2793,315 @@ __webpack_require__.r(__webpack_exports__);
2793
2793
  const toKebabCase = (input) => input.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
2794
2794
 
2795
2795
 
2796
+ /***/ }),
2797
+
2798
+ /***/ "./src/tree/flatten-tree.ts":
2799
+ /*!**********************************!*\
2800
+ !*** ./src/tree/flatten-tree.ts ***!
2801
+ \**********************************/
2802
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
2803
+
2804
+ __webpack_require__.r(__webpack_exports__);
2805
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
2806
+ /* harmony export */ flattenTree: () => (/* binding */ flattenTree)
2807
+ /* harmony export */ });
2808
+ /**
2809
+ * Flattens a hierarchical tree into a single preorder array.
2810
+ *
2811
+ * Each node in the returned list is stripped of its nested children property
2812
+ * (`childrenKey`) and enriched with hierarchy metadata:
2813
+ *
2814
+ * - `parentId` — identifier of the parent node (`undefined` for roots)
2815
+ * - `depthLevel` — nesting depth starting at `0`
2816
+ * - `childCount` — number of direct child nodes
2817
+ *
2818
+ * The flattening order is **preorder**, meaning parents always appear before
2819
+ * their descendants. This representation is especially useful for rendering
2820
+ * tree-based UIs such as TreeSelect components, nested menus, and folder pickers.
2821
+ *
2822
+ * @template OriginItem - Original node shape of the hierarchical structure.
2823
+ *
2824
+ * @param items - Root-level nodes to flatten. If undefined, an empty array is returned.
2825
+ * @param nodeIdKey - Key that uniquely identifies each node.
2826
+ * @param childrenKey - Key containing the nested child node array.
2827
+ *
2828
+ * @param flatTree - Internal accumulator used during recursion.
2829
+ * @param parentId - Parent identifier for the current recursion level.
2830
+ * @param depthLevel - Current depth level, where `0` represents the root.
2831
+ *
2832
+ * @returns A flat preorder list of tree nodes with hierarchy metadata attached,
2833
+ * excluding the original children property.
2834
+ *
2835
+ * @example
2836
+ * ```ts
2837
+ * const tree = [
2838
+ * {
2839
+ * id: 1,
2840
+ * name: 'Root',
2841
+ * children: [{ id: 2, name: 'Child', children: [] }],
2842
+ * },
2843
+ * ];
2844
+ *
2845
+ * const flatTree = flattenTree(tree, 'id', 'children');
2846
+ *
2847
+ * // [
2848
+ * // { id: 1, name: 'Root', parentId: undefined, depthLevel: 0, childCount: 1 },
2849
+ * // { id: 2, name: 'Child', parentId: 1, depthLevel: 1, childCount: 0 }
2850
+ * // ]
2851
+ * ```
2852
+ */
2853
+ const flattenTree = (items, nodeIdKey, childrenKey,
2854
+ ///
2855
+ flatTree = [], parentId = undefined, depthLevel = 0) => {
2856
+ items?.forEach(item => {
2857
+ const { [childrenKey]: _, ...nodeData } = item;
2858
+ const children = item[childrenKey];
2859
+ const hasChildrenArray = Array.isArray(children);
2860
+ flatTree.push({
2861
+ ...nodeData,
2862
+ parentId,
2863
+ depthLevel,
2864
+ childCount: hasChildrenArray ? children.length : 0,
2865
+ });
2866
+ if (hasChildrenArray) {
2867
+ const parentId = item[nodeIdKey];
2868
+ flattenTree(children, nodeIdKey, childrenKey, flatTree, parentId, depthLevel + 1);
2869
+ }
2870
+ });
2871
+ return flatTree;
2872
+ };
2873
+
2874
+
2875
+ /***/ }),
2876
+
2877
+ /***/ "./src/tree/get-tree-children.ts":
2878
+ /*!***************************************!*\
2879
+ !*** ./src/tree/get-tree-children.ts ***!
2880
+ \***************************************/
2881
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
2882
+
2883
+ __webpack_require__.r(__webpack_exports__);
2884
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
2885
+ /* harmony export */ getTreeChildren: () => (/* binding */ getTreeChildren)
2886
+ /* harmony export */ });
2887
+ /**
2888
+ * Returns the direct children of a given parent node from a flattened tree.
2889
+ *
2890
+ * This helper filters a flat tree representation (`HoneyTreeFlatNode`) and
2891
+ * selects only nodes whose `parentId` matches the provided identifier.
2892
+ *
2893
+ * ⚠️ Only direct children are returned - descendants at deeper levels are not included.
2894
+ *
2895
+ * An optional predicate may be provided to further narrow the result set
2896
+ * (e.g. filtering by depth, type, or custom node flags).
2897
+ *
2898
+ * @template OriginItem - Original node shape of the hierarchical structure.
2899
+ * @template ChildrenKey - Key of the removed nested children property.
2900
+ *
2901
+ * @param flatTree - Flat preorder list of tree nodes containing hierarchy metadata.
2902
+ * @param parentId - Identifier of the parent node whose direct children should be returned.
2903
+ * @param predicate - Optional additional filter applied to each matched child node.
2904
+ *
2905
+ * @returns An array of nodes that are direct children of the specified parent.
2906
+ *
2907
+ * @example
2908
+ * ```ts
2909
+ * const children = getTreeChildren(flatTree, 1);
2910
+ *
2911
+ * // Returns all nodes where parentId === 1
2912
+ * ```
2913
+ *
2914
+ * @example
2915
+ * ```ts
2916
+ * const shallowChildren = getTreeChildren(
2917
+ * flatTree,
2918
+ * 1,
2919
+ * node => node.depthLevel <= 2,
2920
+ * );
2921
+ *
2922
+ * // Returns only children of node 1 that satisfy the predicate.
2923
+ * ```
2924
+ */
2925
+ const getTreeChildren = (flatTree, parentId, predicate) => flatTree.filter(node => node.parentId === parentId && (!predicate || predicate(node)));
2926
+
2927
+
2928
+ /***/ }),
2929
+
2930
+ /***/ "./src/tree/index.ts":
2931
+ /*!***************************!*\
2932
+ !*** ./src/tree/index.ts ***!
2933
+ \***************************/
2934
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
2935
+
2936
+ __webpack_require__.r(__webpack_exports__);
2937
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
2938
+ /* harmony export */ flattenTree: () => (/* reexport safe */ _flatten_tree__WEBPACK_IMPORTED_MODULE_1__.flattenTree),
2939
+ /* harmony export */ getTreeChildren: () => (/* reexport safe */ _get_tree_children__WEBPACK_IMPORTED_MODULE_2__.getTreeChildren),
2940
+ /* harmony export */ searchTree: () => (/* reexport safe */ _search_tree__WEBPACK_IMPORTED_MODULE_3__.searchTree)
2941
+ /* harmony export */ });
2942
+ /* harmony import */ var _tree_types__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./tree.types */ "./src/tree/tree.types.ts");
2943
+ /* harmony import */ var _flatten_tree__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./flatten-tree */ "./src/tree/flatten-tree.ts");
2944
+ /* harmony import */ var _get_tree_children__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./get-tree-children */ "./src/tree/get-tree-children.ts");
2945
+ /* harmony import */ var _search_tree__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./search-tree */ "./src/tree/search-tree.ts");
2946
+
2947
+
2948
+
2949
+
2950
+
2951
+
2952
+ /***/ }),
2953
+
2954
+ /***/ "./src/tree/search-tree.ts":
2955
+ /*!*********************************!*\
2956
+ !*** ./src/tree/search-tree.ts ***!
2957
+ \*********************************/
2958
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
2959
+
2960
+ __webpack_require__.r(__webpack_exports__);
2961
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
2962
+ /* harmony export */ searchTree: () => (/* binding */ searchTree)
2963
+ /* harmony export */ });
2964
+ /* harmony import */ var _guards__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ~/guards */ "./src/guards.ts");
2965
+ /* harmony import */ var _string__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ~/string */ "./src/string/index.ts");
2966
+
2967
+
2968
+ /**
2969
+ * Performs a context-aware search over a flattened tree.
2970
+ *
2971
+ * Matching is applied to the provided string field (`nodeValueKey`) using
2972
+ * **case-insensitive, word-prefix comparison**:
2973
+ *
2974
+ * - The query is split into words
2975
+ * - Each query word must match the start of at least one word in the node value
2976
+ *
2977
+ * Unlike a simple filter, this function preserves hierarchical context:
2978
+ *
2979
+ * - When a nested node matches, all of its ancestor nodes are included, so the
2980
+ * result remains navigable.
2981
+ * - When a root node matches, its full descendant subtree is included so the
2982
+ * hierarchy stays intact.
2983
+ *
2984
+ * This makes the utility ideal for searchable tree-based UIs such as
2985
+ * TreeSelect components, folder pickers, nested menus, and grouped lists.
2986
+ *
2987
+ * @template OriginItem - Original node shape of the hierarchical structure.
2988
+ * @template ChildrenKey - Key of the removed nested children property.
2989
+ *
2990
+ * @param flatTree - Flat preorder list of tree nodes containing hierarchy metadata.
2991
+ * @param nodeIdKey - Key that uniquely identifies each node.
2992
+ * @param nodeValueKey - Key containing the searchable string value (e.g. `"name"`).
2993
+ * @param searchQuery - User input query. Split into words and matched by prefix.
2994
+ *
2995
+ * @returns A filtered flat list containing:
2996
+ * - all matching nodes
2997
+ * - their ancestor chain (for nested matches)
2998
+ * - full descendant subtrees (for root-level matches)
2999
+ *
3000
+ * @example
3001
+ * ```ts
3002
+ * const results = searchTree(flatTree, 'id', 'label', 'kit che');
3003
+ *
3004
+ * // Matches nodes where words start with "kit" and "che"
3005
+ * // (e.g. "Kitchen Chair"), including their parents.
3006
+ * ```
3007
+ *
3008
+ * @example
3009
+ * ```ts
3010
+ * // If a deep child matches, ancestors are included:
3011
+ * //
3012
+ * // Root
3013
+ * // └─ Category
3014
+ * // └─ Item ← matches
3015
+ * //
3016
+ * // Result includes: Root, Category, Item
3017
+ * ```
3018
+ */
3019
+ const searchTree = (flatTree, nodeIdKey, nodeValueKey, searchQuery) => {
3020
+ const searchWords = (0,_string__WEBPACK_IMPORTED_MODULE_1__.splitStringIntoWords)(searchQuery.toLowerCase());
3021
+ if (!searchWords.length) {
3022
+ return flatTree;
3023
+ }
3024
+ const nodeIdToIndexMap = flatTree.reduce((result, node, nodeIndex) => {
3025
+ // Map node id → index for fast ancestor lookup.
3026
+ result[node[nodeIdKey]] = nodeIndex;
3027
+ return result;
3028
+ }, {});
3029
+ return flatTree.reduce((matchedNodes, node) => {
3030
+ const nodeValue = node[nodeValueKey];
3031
+ // If the item value is null, undefined or empty string
3032
+ if (!nodeValue) {
3033
+ return matchedNodes;
3034
+ }
3035
+ if (matchedNodes.some(matchedNode => matchedNode[nodeIdKey] === node[nodeIdKey])) {
3036
+ return matchedNodes;
3037
+ }
3038
+ const nodeValueWords = (0,_string__WEBPACK_IMPORTED_MODULE_1__.splitStringIntoWords)(nodeValue.toLowerCase());
3039
+ const isNodeMatched = searchWords.every(searchWord => nodeValueWords.some(word => word.startsWith(searchWord)));
3040
+ if (isNodeMatched) {
3041
+ if ((0,_guards__WEBPACK_IMPORTED_MODULE_0__.isUndefined)(node.parentId)) {
3042
+ matchedNodes.push(node);
3043
+ const insertNestedItems = (targetNode) => {
3044
+ if (!targetNode.childCount) {
3045
+ return;
3046
+ }
3047
+ flatTree.forEach(node => {
3048
+ if (node.parentId === targetNode[nodeIdKey]) {
3049
+ matchedNodes.push(node);
3050
+ insertNestedItems(node);
3051
+ }
3052
+ });
3053
+ };
3054
+ insertNestedItems(node);
3055
+ }
3056
+ else {
3057
+ const insertParentNodes = (targetNode) => {
3058
+ const parentNodeIndex = nodeIdToIndexMap[targetNode.parentId];
3059
+ const parentNode = flatTree[parentNodeIndex];
3060
+ if (!(0,_guards__WEBPACK_IMPORTED_MODULE_0__.isUndefined)(parentNode.parentId)) {
3061
+ insertParentNodes(parentNode);
3062
+ }
3063
+ const prevNodeParentId = matchedNodes.length
3064
+ ? matchedNodes[matchedNodes.length - 1].parentId
3065
+ : null;
3066
+ const shouldInsertParentNode = (0,_guards__WEBPACK_IMPORTED_MODULE_0__.isNull)(prevNodeParentId) || prevNodeParentId !== targetNode.parentId;
3067
+ if (shouldInsertParentNode) {
3068
+ (0,_guards__WEBPACK_IMPORTED_MODULE_0__.assert)(parentNode, '[@react-hive/honey-utils]: Parent node was not found.');
3069
+ matchedNodes.push(parentNode);
3070
+ }
3071
+ };
3072
+ insertParentNodes(node);
3073
+ matchedNodes.push(node);
3074
+ }
3075
+ }
3076
+ return matchedNodes;
3077
+ }, []);
3078
+ };
3079
+
3080
+
3081
+ /***/ }),
3082
+
3083
+ /***/ "./src/tree/tree.types.ts":
3084
+ /*!********************************!*\
3085
+ !*** ./src/tree/tree.types.ts ***!
3086
+ \********************************/
3087
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
3088
+
3089
+ __webpack_require__.r(__webpack_exports__);
3090
+
3091
+
3092
+
3093
+ /***/ }),
3094
+
3095
+ /***/ "./src/types.ts":
3096
+ /*!**********************!*\
3097
+ !*** ./src/types.ts ***!
3098
+ \**********************/
3099
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
3100
+
3101
+ __webpack_require__.r(__webpack_exports__);
3102
+
3103
+
3104
+
2796
3105
  /***/ })
2797
3106
 
2798
3107
  /******/ });
@@ -2884,12 +3193,14 @@ __webpack_require__.r(__webpack_exports__);
2884
3193
  /* harmony export */ filterSequential: () => (/* reexport safe */ _async__WEBPACK_IMPORTED_MODULE_2__.filterSequential),
2885
3194
  /* harmony export */ findAsync: () => (/* reexport safe */ _async__WEBPACK_IMPORTED_MODULE_2__.findAsync),
2886
3195
  /* harmony export */ findCharIndices: () => (/* reexport safe */ _string__WEBPACK_IMPORTED_MODULE_13__.findCharIndices),
3196
+ /* harmony export */ flattenTree: () => (/* reexport safe */ _tree__WEBPACK_IMPORTED_MODULE_14__.flattenTree),
2887
3197
  /* harmony export */ forEachChar: () => (/* reexport safe */ _string__WEBPACK_IMPORTED_MODULE_13__.forEachChar),
2888
3198
  /* harmony export */ generateEphemeralId: () => (/* reexport safe */ _id__WEBPACK_IMPORTED_MODULE_9__.generateEphemeralId),
2889
3199
  /* harmony export */ getDOMRectIntersectionRatio: () => (/* reexport safe */ _intersection__WEBPACK_IMPORTED_MODULE_10__.getDOMRectIntersectionRatio),
2890
3200
  /* harmony export */ getElementOffsetRect: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_3__.getElementOffsetRect),
2891
3201
  /* harmony export */ getFocusableHtmlElements: () => (/* reexport safe */ _a11y__WEBPACK_IMPORTED_MODULE_0__.getFocusableHtmlElements),
2892
3202
  /* harmony export */ getLocalStorageCapabilities: () => (/* reexport safe */ _env__WEBPACK_IMPORTED_MODULE_4__.getLocalStorageCapabilities),
3203
+ /* harmony export */ getTreeChildren: () => (/* reexport safe */ _tree__WEBPACK_IMPORTED_MODULE_14__.getTreeChildren),
2893
3204
  /* harmony export */ getWordsInitials: () => (/* reexport safe */ _string__WEBPACK_IMPORTED_MODULE_13__.getWordsInitials),
2894
3205
  /* harmony export */ getXOverflowWidth: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_3__.getXOverflowWidth),
2895
3206
  /* harmony export */ getYOverflowHeight: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_3__.getYOverflowHeight),
@@ -2942,6 +3253,7 @@ __webpack_require__.r(__webpack_exports__);
2942
3253
  /* harmony export */ retry: () => (/* reexport safe */ _async__WEBPACK_IMPORTED_MODULE_2__.retry),
2943
3254
  /* harmony export */ runParallel: () => (/* reexport safe */ _async__WEBPACK_IMPORTED_MODULE_2__.runParallel),
2944
3255
  /* harmony export */ runSequential: () => (/* reexport safe */ _async__WEBPACK_IMPORTED_MODULE_2__.runSequential),
3256
+ /* harmony export */ searchTree: () => (/* reexport safe */ _tree__WEBPACK_IMPORTED_MODULE_14__.searchTree),
2945
3257
  /* harmony export */ someAsync: () => (/* reexport safe */ _async__WEBPACK_IMPORTED_MODULE_2__.someAsync),
2946
3258
  /* harmony export */ splitStringIntoWords: () => (/* reexport safe */ _string__WEBPACK_IMPORTED_MODULE_13__.splitStringIntoWords),
2947
3259
  /* harmony export */ timeout: () => (/* reexport safe */ _async__WEBPACK_IMPORTED_MODULE_2__.timeout),
@@ -2963,6 +3275,10 @@ __webpack_require__.r(__webpack_exports__);
2963
3275
  /* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./math */ "./src/math/index.ts");
2964
3276
  /* harmony import */ var _object__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./object */ "./src/object.ts");
2965
3277
  /* harmony import */ var _string__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./string */ "./src/string/index.ts");
3278
+ /* harmony import */ var _tree__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./tree */ "./src/tree/index.ts");
3279
+ /* harmony import */ var _types__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./types */ "./src/types.ts");
3280
+
3281
+
2966
3282
 
2967
3283
 
2968
3284