@geneui/components 3.0.0-next-e06ec94-05082025 → 3.0.0-next-67f77e7-11082025

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/Menu.js CHANGED
@@ -1,4 +1,4 @@
1
- import React__default, { createContext, useState, useRef, useContext, useEffect, useMemo, Children, cloneElement } from 'react';
1
+ import React__default, { createContext, useState, useRef, useContext, useEffect, useMemo, Children, cloneElement, isValidElement } from 'react';
2
2
  import { c as classNames } from './index-ce02421b.js';
3
3
  import Loader from './Loader.js';
4
4
  import { P as Popover, a as PopoverBody } from './PopoverBody-7437f3ac.js';
@@ -60,12 +60,12 @@ const findPathOfSelected = (menu, path = []) => {
60
60
  const MENU_GAP_FROM_TARGET = 4;
61
61
  const cloneChildrenRecursive = (children, paths, props = {}, pathID = "") => {
62
62
  return Children.map(children, (child, i) => {
63
- var _a, _b, _c;
63
+ var _a, _b;
64
64
  const generatedId = pathID ? `${pathID}_${i}` : `${i}`;
65
65
  return cloneElement(child, Object.assign(Object.assign({}, props), { generatedId,
66
- paths }), Array.isArray((_a = child.props) === null || _a === void 0 ? void 0 : _a.children)
67
- ? cloneChildrenRecursive((_b = child.props) === null || _b === void 0 ? void 0 : _b.children, paths, props, generatedId)
68
- : (_c = child.props) === null || _c === void 0 ? void 0 : _c.children);
66
+ paths }), child.props.children && typeof child.props.children !== "string"
67
+ ? cloneChildrenRecursive((_a = child.props) === null || _a === void 0 ? void 0 : _a.children, paths, props, generatedId)
68
+ : (_b = child.props) === null || _b === void 0 ? void 0 : _b.children);
69
69
  });
70
70
  };
71
71
  const MenuContext = createContext({});
@@ -127,7 +127,7 @@ const Menu = ({ onChange, children, loading, loadingText, swappable, setPropsFor
127
127
  if (onOpenChange) {
128
128
  onOpenChange(isOpenState);
129
129
  }
130
- }, [isOpenState]);
130
+ }, [isOpenState, onOpenChange]);
131
131
  const onChangeHandler = ({ generatedId, isBack, closeMenu, item }) => {
132
132
  if (swappable || isMobileBreakpoint)
133
133
  setOpenSelectedPathState(false);
@@ -142,7 +142,7 @@ const Menu = ({ onChange, children, loading, loadingText, swappable, setPropsFor
142
142
  else {
143
143
  setPaths(currentPath);
144
144
  }
145
- if (closeMenu) {
145
+ if (closeMenu && onChange) {
146
146
  onChange(Object.assign(Object.assign({}, item), { title: typeof (item === null || item === void 0 ? void 0 : item.children) === "string" ? item === null || item === void 0 ? void 0 : item.children : item === null || item === void 0 ? void 0 : item.title }));
147
147
  }
148
148
  };
@@ -157,7 +157,7 @@ const Menu = ({ onChange, children, loading, loadingText, swappable, setPropsFor
157
157
  relativeRefsSetter,
158
158
  openSelectedPath: openSelectedPathState,
159
159
  size: size
160
- }), [onChangeHandler, swappable, size, isMobileBreakpoint, relativeRefsSetter, openSelectedPath]);
160
+ }), [onChangeHandler, swappable, size, isMobileBreakpoint, relativeRefsSetter, openSelectedPathState]);
161
161
  const clonedChildren = cloneChildrenRecursive(children, paths);
162
162
  const onScrollHandler = () => {
163
163
  if (isMobileBreakpoint)
@@ -242,7 +242,7 @@ const MenuItemButton = ({ type, onItemClickHandler, disabled, children, danger,
242
242
  };
243
243
 
244
244
  const MenuItem = (props) => {
245
- const { children, title, activeElement, selected, IconBefore, IconAfter, danger, disabled, id, divider, ComponentRender, emptyText = "No data to show", paths, generatedId, loading, loadingText } = props;
245
+ const { children, title, activeElement, selected, IconBefore, IconAfter, danger, disabled, id, divider, ComponentRender, emptyText = "No data to show", paths, generatedId, loading, loadingText, render } = props;
246
246
  const [propsForPopover, setPropsForPopover] = useState({});
247
247
  const parentRef = useRef(null);
248
248
  const { onChangeHandler, swappable, relativeRefsSetter, size, openSelectedPath } = useContext(MenuContext);
@@ -267,7 +267,14 @@ const MenuItem = (props) => {
267
267
  });
268
268
  const onItemClickHandler = (isBack) => {
269
269
  if (onChangeHandler && generatedId) {
270
- onChangeHandler({ generatedId, id, isBack, closeMenu: typeof children === "string", item: props });
270
+ const shouldCloseMenu = !!render || typeof children === "string";
271
+ onChangeHandler({
272
+ generatedId,
273
+ id,
274
+ isBack,
275
+ closeMenu: shouldCloseMenu,
276
+ item: props
277
+ });
271
278
  }
272
279
  };
273
280
  useEffect(() => {
@@ -314,28 +321,51 @@ const MenuItem = (props) => {
314
321
  }
315
322
  return React__default.createElement("div", { className: "menu__content" }, children);
316
323
  }, [children, emptyText, loading, loadingText, swappable, isActiveSwappableContent]);
324
+ const renderMenuItem = (type) => {
325
+ if (render && type === "simple") {
326
+ const renderedElement = render({ id, title: children });
327
+ if (isValidElement(renderedElement)) {
328
+ const originalOnClick = renderedElement.props
329
+ .onClick;
330
+ const propsToApply = {
331
+ className: classNames("menu__item", renderedElement.props.className, {
332
+ menu__item_danger: danger,
333
+ menu__item_disabled: disabled,
334
+ menu__item_selected: selected,
335
+ menu__item_icon_after: !!IconAfter,
336
+ menu__item_icon_before: !!IconBefore
337
+ }),
338
+ onClick: (event) => {
339
+ originalOnClick === null || originalOnClick === void 0 ? void 0 : originalOnClick(event);
340
+ onItemClickHandler(false);
341
+ },
342
+ disabled
343
+ };
344
+ return cloneElement(renderedElement, propsToApply, children);
345
+ }
346
+ }
347
+ if (type === "custom" && CustomElement) {
348
+ return CustomElement;
349
+ }
350
+ return (React__default.createElement(MenuItemButton, { type: type, onItemClickHandler: onItemClickHandler, title: title, IconBefore: IconBefore, IconAfter: IconAfter, disabled: disabled, danger: danger, propsForPopover: propsForPopover, active: popoverOpenState, divider: divider, selected: selected }, children));
351
+ };
317
352
  return swappable ? (React__default.createElement(React__default.Fragment, null, typeof children !== "string" ? (React__default.createElement(React__default.Fragment, null,
318
- isActiveSwappableContent && !popoverOpenState && (React__default.createElement(MenuItemButton, { type: "parent", onItemClickHandler: onItemClickHandler, title: title, IconBefore: IconBefore, IconAfter: IconAfter, disabled: disabled, danger: danger, propsForPopover: propsForPopover, active: popoverOpenState, divider: divider })),
353
+ isActiveSwappableContent && !popoverOpenState && renderMenuItem("parent"),
319
354
  React__default.createElement("div", { className: classNames("menu__list", {
320
355
  menu__list_current: popoverOpenState,
321
356
  menu__list_hidden: !popoverOpenState
322
357
  }) },
323
358
  isActiveSwappableContent && (React__default.createElement("div", { className: "menu__header" },
324
359
  React__default.createElement(MenuItemButton, { type: "header", onItemClickHandler: onItemClickHandler, title: title }))),
325
- menuContent))) : (
326
- // Simple menu item
327
- isActiveSwappableContent &&
328
- (CustomElement || (React__default.createElement(MenuItemButton, { type: "simple", onItemClickHandler: onItemClickHandler, danger: danger, selected: selected, disabled: disabled, IconBefore: IconBefore, IconAfter: IconAfter, divider: divider }, children)))))) : (React__default.createElement(React__default.Fragment, null, typeof children !== "string" ? (React__default.createElement(React__default.Fragment, null,
329
- React__default.createElement(MenuItemButton, { type: "parent", onItemClickHandler: onItemClickHandler, title: title, IconBefore: IconBefore, IconAfter: IconAfter, disabled: disabled, danger: danger, propsForPopover: propsForPopover, active: popoverOpenState, divider: divider }),
360
+ menuContent))) : (isActiveSwappableContent && renderMenuItem(ComponentRender ? "custom" : "simple")))) : (React__default.createElement(React__default.Fragment, null, typeof children !== "string" ? (React__default.createElement(React__default.Fragment, null,
361
+ renderMenuItem("parent"),
330
362
  React__default.createElement(Popover, { setProps: setPropsForPopover, size: popoverSizeMapping[size], position: isRTLMode ? "left-top" : "right-top", withArrow: false, margin: 10, open: popoverOpenState, ref: popoverFloatingRef },
331
363
  React__default.createElement(PopoverBody, { withPadding: false, className: `menu__body menu__body_size menu__body_size_${size}`, withScrollbar: false },
332
364
  React__default.createElement("div", { ref: parentRef, className: classNames("menu__list", {
333
365
  menu__list_current: activeElement,
334
366
  menu__item_disabled: disabled
335
367
  }) },
336
- React__default.createElement(Scrollbar, { className: "menu__scrollbar", onScroll: onScrollHandler, scrollToTop: activeElementPosition, scrollBehaviorSmooth: false }, menuContent)))))) : (
337
- // Simple menu item
338
- CustomElement || (React__default.createElement(MenuItemButton, { type: "simple", onItemClickHandler: onItemClickHandler, danger: danger, selected: selected, disabled: disabled, IconBefore: IconBefore, IconAfter: IconAfter, divider: divider }, children)))));
368
+ React__default.createElement(Scrollbar, { className: "menu__scrollbar", onScroll: onScrollHandler, scrollToTop: activeElementPosition, scrollBehaviorSmooth: false }, menuContent)))))) : (renderMenuItem(ComponentRender ? "custom" : "simple"))));
339
369
  };
340
370
 
341
371
  export { Menu, MenuItem };
package/Navigation.js CHANGED
@@ -1,4 +1,4 @@
1
- import React__default, { useRef, useEffect, useState, useContext, Fragment } from 'react';
1
+ import React__default, { useState, useRef, useEffect, isValidElement, cloneElement, useContext, Fragment } from 'react';
2
2
  import { c as classNames } from './index-ce02421b.js';
3
3
  import { _ as _objectWithoutProperties, a as _extends } from './ActivityRecent-b88e2ba8.js';
4
4
  import { S as SvgThreeDotsHorizontal } from './ThreeDotsHorizontal-479c2b4b.js';
@@ -77,8 +77,8 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
77
77
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
78
78
  };
79
79
 
80
- const NavigationItem = ({ title, children, onClick, disabled, selected, depth, Icon, path }) => {
81
- const [isNavItemOpen, setIsNavItemOpen] = React__default.useState(false);
80
+ const NavigationItem = ({ title, children, onClick, disabled, selected, depth, Icon, path, render }) => {
81
+ const [isNavItemOpen, setIsNavItemOpen] = useState(false);
82
82
  const textRef = useRef(null);
83
83
  const isTruncated = useEllipsisDetection(textRef, [title]);
84
84
  const onClickHandler = () => {
@@ -92,23 +92,41 @@ const NavigationItem = ({ title, children, onClick, disabled, selected, depth, I
92
92
  return;
93
93
  setIsNavItemOpen(selected);
94
94
  }, [title, selected]);
95
+ const itemContent = (React__default.createElement(React__default.Fragment, null,
96
+ Icon && React__default.createElement(Icon, { className: "navigationItem__icon", size: 20 }),
97
+ React__default.createElement(Tooltip, { text: title, isVisible: isTruncated },
98
+ React__default.createElement(Text, { as: "span", variant: "labelMediumMedium", className: "menu__title ellipsis-text", ref: textRef }, title)),
99
+ children && (React__default.createElement(SvgChevronRight, { className: classNames("navigationItem__chevron", {
100
+ navigationItem__chevron_open: isNavItemOpen
101
+ }), size: 20 }))));
102
+ const propsToApply = {
103
+ "aria-expanded": children ? isNavItemOpen : undefined,
104
+ disabled,
105
+ className: classNames("navigationItem__button", `navigationItem__button_shift_${depth}`, {
106
+ navigationItem__button_disabled: disabled,
107
+ navigationItem__button_selected: selected,
108
+ navigationItem__button_pointer_none: selected && !children,
109
+ navigationItem__button_selected_noChildren: !children && selected,
110
+ navigationItem__button_render: render && path
111
+ }),
112
+ onClick: onClickHandler
113
+ };
114
+ const linkData = { path, title, isActive: selected, hasChildren: !!children, isDisabled: disabled };
115
+ const interactiveElement = (() => {
116
+ if (render && path) {
117
+ const renderedElement = render(linkData);
118
+ if (isValidElement(renderedElement)) {
119
+ return cloneElement(renderedElement, Object.assign({}, propsToApply), itemContent);
120
+ }
121
+ }
122
+ return (React__default.createElement("button", Object.assign({ type: "button" }, propsToApply), itemContent));
123
+ })();
95
124
  return (React__default.createElement("div", { className: "navigationItem" },
96
- React__default.createElement("button", { disabled: disabled, type: "button", className: classNames("navigationItem__button", `navigationItem__button_shift_${depth}`, {
97
- navigationItem__button_disabled: disabled,
98
- navigationItem__button_selected: selected,
99
- navigationItem__button_pointer_none: selected && !children,
100
- navigationItem__button_selected_noChildren: !children && selected
101
- }), onClick: onClickHandler },
102
- Icon && React__default.createElement(Icon, { className: "navigationItem__icon", size: 20 }),
103
- React__default.createElement(Tooltip, { text: title, isVisible: isTruncated },
104
- React__default.createElement(Text, { as: "span", variant: "labelMediumMedium", className: "menu__title ellipsis-text", ref: textRef }, title)),
105
- children && (React__default.createElement(SvgChevronRight, { className: classNames("navigationItem__chevron", {
106
- navigationItem__chevron_open: isNavItemOpen
107
- }), size: 20 }))),
125
+ interactiveElement,
108
126
  isNavItemOpen && children));
109
127
  };
110
128
 
111
- const NavigationColItem = ({ Icon, selected, disabled, title, onClick, index, onMouseEnter, propsForPopover = {}, opened, path, isVisible, hasChildren, currentSelected }) => {
129
+ const NavigationColItem = ({ Icon, selected, disabled, title, onClick, index, onMouseEnter, propsForPopover = {}, opened, path, isVisible, hasChildren, currentSelected, render }) => {
112
130
  const textRef = useRef(null);
113
131
  const isTruncated = useEllipsisDetection(textRef, [title]);
114
132
  const onClickHandler = (activeIndex) => {
@@ -121,20 +139,39 @@ const NavigationColItem = ({ Icon, selected, disabled, title, onClick, index, on
121
139
  onMouseEnter(activeIndex);
122
140
  }
123
141
  };
142
+ const propsToApply = Object.assign(Object.assign({}, propsForPopover), { "aria-label": title, disabled: disabled || !isVisible, className: classNames("navigation__iconButton", {
143
+ navigation__iconButton_selected: selected,
144
+ navigation__iconButton_currentSelected: currentSelected && !selected && opened,
145
+ navigation__iconButton_pointer_none: selected && !hasChildren,
146
+ navigation__iconButton_disabled: disabled
147
+ }), onClick: () => onClickHandler(index || 0), onMouseEnter: () => onMouseEnterHandler(index || 0) });
148
+ const linkData = {
149
+ path,
150
+ title,
151
+ Icon,
152
+ isActive: selected,
153
+ hasChildren,
154
+ isDisabled: disabled
155
+ };
156
+ const interactiveElement = (() => {
157
+ if (render && path) {
158
+ const renderedElement = render(linkData);
159
+ if (isValidElement(renderedElement)) {
160
+ return cloneElement(renderedElement, Object.assign({}, propsToApply), Icon && React__default.createElement(Icon, null));
161
+ }
162
+ }
163
+ return (React__default.createElement("button", Object.assign({ type: "button" }, propsToApply), Icon && React__default.createElement(Icon, null)));
164
+ })();
124
165
  return (React__default.createElement("div", { className: "navigation__listItem" },
125
- React__default.createElement("button", Object.assign({ type: "button", disabled: disabled || !isVisible, className: classNames("navigation__iconButton", {
126
- navigation__iconButton_selected: selected,
127
- navigation__iconButton_currentSelected: currentSelected && !selected && opened,
128
- navigation__iconButton_pointer_none: selected && !hasChildren,
129
- navigation__iconButton_disabled: disabled
130
- }), onClick: () => onClickHandler(index), onMouseEnter: () => onMouseEnterHandler(index) }, propsForPopover), Icon && React__default.createElement(Icon, null)),
166
+ interactiveElement,
131
167
  title && (React__default.createElement(Tooltip, { text: title, isVisible: isTruncated },
132
168
  React__default.createElement(Text, { as: "p", variant: "captionMediumMedium", className: "navigation__listItemText ellipsis-text", ref: textRef }, title)))));
133
169
  };
134
170
 
135
- var css_248z = ".navigation{display:flex;height:100%;width:100%}.navigation__list{display:flex;height:100%;width:var(--guit-sem-dimension-width-3xlarge)}.navigation__listWrapper{align-items:center;border-inline-end:var(--guit-ref-border-width-thin) var(--guit-ref-border-style-solid) var(--guit-sem-color-border-neutral-2);display:flex;flex-direction:column;overflow:hidden;padding:var(--guit-ref-spacing-xsmall) var(--guit-ref-spacing-xsmall) var(--guit-ref-spacing-2xlarge) var(--guit-ref-spacing-xsmall);width:100%}.navigation__listWrapper>.navigation__listItem{margin-block:var(--guit-ref-spacing-xsmall)}.navigation__listItems{display:flex;flex-flow:column wrap;gap:var(--guit-ref-spacing-xsmall);max-width:var(--guit-sem-dimension-height-xlarge);min-height:0}.navigation__listItem{align-items:center;display:flex;flex-direction:column;width:100%}.navigation__addButton{align-items:center;display:flex;flex:0 0 auto;flex-direction:column;height:var(--guit-sem-dimension-height-large);margin-top:auto;width:var(--guit-sem-dimension-width-large)}.navigation__listItemText{color:var(--guit-sem-color-foreground-neutral-1);max-width:100%;-webkit-user-select:none;user-select:none}.navigation__iconButton{border-radius:var(--guit-ref-radius-3xsmall);height:var(--guit-sem-dimension-height-large);padding:var(--guit-ref-spacing-xsmall);width:var(--guit-sem-dimension-width-large)}.navigation__iconButton_selected{background-color:var(--guit-sem-color-background-selected-2);color:var(--guit-sem-color-foreground-inverse)}.navigation__iconButton_currentSelected{background-color:var(--guit-sem-color-background-neutral-1-pressed)}.navigation__iconButton_disabled{background-color:var(--guit-sem-color-background-neutral-1);pointer-events:none}.navigation__iconButton_disabled,.navigation__iconButton_disabled+.navigation__listItemText{color:var(--guit-sem-color-foreground-disabled)}.navigation__iconButton_pointer_none{pointer-events:none}.navigation__iconButton:not(.navigation__iconButton_selected,.navigation__iconButton_disabled){color:var(--guit-sem-color-foreground-neutral-2)}.navigation__iconButton:not(.navigation__iconButton_selected,.navigation__iconButton_disabled):hover{background-color:var(--guit-sem-color-background-neutral-1-hover)}.navigation__iconButton:not(.navigation__iconButton_selected,.navigation__iconButton_disabled):active{background-color:var(--guit-sem-color-background-neutral-1-pressed)}.navigation__divider{flex:0 0 auto}.navigation__menu{border-inline-end:var(--guit-ref-border-width-thin) var(--guit-ref-border-style-solid) var(--guit-sem-color-border-neutral-2);display:flex;height:100%;width:20rem}.navigation__menu_wrapper{height:100%;padding:var(--guit-ref-spacing-3xsmall) var(--guit-ref-spacing-xsmall)}.navigationItem,.navigation__menu_wrapper{display:flex;flex-direction:column;gap:var(--guit-ref-spacing-3xsmall);width:100%}.navigationItem__button{border-radius:var(--guit-ref-radius-3xsmall);display:flex;gap:var(--guit-ref-spacing-xsmall);padding:var(--guit-ref-spacing-xsmall) var(--guit-ref-spacing-xsmall) var(--guit-ref-spacing-xsmall) var(--guit-ref-spacing-medium);width:100%}.navigationItem__button:hover{background-color:var(--guit-sem-color-background-neutral-1-hover)}.navigationItem__button:active{background-color:var(--guit-sem-color-background-neutral-1-pressed)}.navigationItem__button_selected{color:var(--guit-sem-color-foreground-selected)}.navigationItem__button_selected_noChildren{background:var(--guit-sem-color-background-neutral-3);color:var(--guit-sem-color-foreground-accent-blue)}.navigationItem__button_pointer_none{pointer-events:none}.navigationItem__button_disabled{background-color:var(--guit-sem-color-background-neutral-1);color:var(--guit-sem-color-foreground-disabled);pointer-events:none}.navigationItem__button_shift_1{padding-inline-start:var(--guit-ref-spacing-5xlarge)}.navigationItem__button_shift_2{padding-inline-start:var(--guit-ref-spacing-8xlarge)}.navigationItem__button_shift_3{padding-inline-start:var(--guit-ref-spacing-9xlarge)}.navigationItem__button_shift_4{padding-inline-start:var(--guit-ref-spacing-10xlarge)}.navigationItem__chevron{margin-left:auto}.navigationItem__chevron_open{transform:rotate(90deg)}";
171
+ var css_248z = ".navigation{display:flex;height:100%;width:100%}.navigation__list{display:flex;height:100%;width:var(--guit-sem-dimension-width-3xlarge)}.navigation__listWrapper{align-items:center;border-inline-end:var(--guit-ref-border-width-thin) var(--guit-ref-border-style-solid) var(--guit-sem-color-border-neutral-2);display:flex;flex-direction:column;overflow:hidden;padding:var(--guit-ref-spacing-xsmall) var(--guit-ref-spacing-xsmall) var(--guit-ref-spacing-2xlarge) var(--guit-ref-spacing-xsmall);width:100%}.navigation__listWrapper>.navigation__listItem{margin-block:var(--guit-ref-spacing-xsmall)}.navigation__listItems{display:flex;flex-flow:column wrap;gap:var(--guit-ref-spacing-xsmall);max-width:var(--guit-sem-dimension-height-xlarge);min-height:0}.navigation__listItem{align-items:center;display:flex;flex-direction:column;width:100%}.navigation__addButton{align-items:center;display:flex;flex:0 0 auto;flex-direction:column;height:var(--guit-sem-dimension-height-large);margin-top:auto;width:var(--guit-sem-dimension-width-large)}.navigation__listItemText{color:var(--guit-sem-color-foreground-neutral-1);max-width:100%;-webkit-user-select:none;user-select:none}.navigation__iconButton{border-radius:var(--guit-ref-radius-3xsmall);height:var(--guit-sem-dimension-height-large);padding:var(--guit-ref-spacing-xsmall);width:var(--guit-sem-dimension-width-large)}.navigation__iconButton_selected{background-color:var(--guit-sem-color-background-selected-2);color:var(--guit-sem-color-foreground-inverse)}.navigation__iconButton_currentSelected{background-color:var(--guit-sem-color-background-neutral-1-pressed)}.navigation__iconButton_disabled{background-color:var(--guit-sem-color-background-neutral-1);pointer-events:none}.navigation__iconButton_disabled,.navigation__iconButton_disabled+.navigation__listItemText{color:var(--guit-sem-color-foreground-disabled)}.navigation__iconButton_pointer_none{pointer-events:none}.navigation__iconButton:not(.navigation__iconButton_selected,.navigation__iconButton_disabled){color:var(--guit-sem-color-foreground-neutral-2)}.navigation__iconButton:not(.navigation__iconButton_selected,.navigation__iconButton_disabled):hover{background-color:var(--guit-sem-color-background-neutral-1-hover)}.navigation__iconButton:not(.navigation__iconButton_selected,.navigation__iconButton_disabled):active{background-color:var(--guit-sem-color-background-neutral-1-pressed)}.navigation__divider{flex:0 0 auto}.navigation__menu{border-inline-end:var(--guit-ref-border-width-thin) var(--guit-ref-border-style-solid) var(--guit-sem-color-border-neutral-2);display:flex;height:100%;width:20rem}.navigation__menu_wrapper{height:100%;padding:var(--guit-ref-spacing-3xsmall) var(--guit-ref-spacing-xsmall)}.navigationItem,.navigation__menu_wrapper{display:flex;flex-direction:column;gap:var(--guit-ref-spacing-3xsmall);width:100%}.navigationItem__button{border-radius:var(--guit-ref-radius-3xsmall);display:flex;gap:var(--guit-ref-spacing-xsmall);padding:var(--guit-ref-spacing-xsmall) var(--guit-ref-spacing-xsmall) var(--guit-ref-spacing-xsmall) var(--guit-ref-spacing-medium);width:100%}.navigationItem__button_render{color:inherit}.navigationItem__button:hover{background-color:var(--guit-sem-color-background-neutral-1-hover)}.navigationItem__button:active{background-color:var(--guit-sem-color-background-neutral-1-pressed)}.navigationItem__button_selected{color:var(--guit-sem-color-foreground-selected)}.navigationItem__button_selected_noChildren{background:var(--guit-sem-color-background-neutral-3);color:var(--guit-sem-color-foreground-accent-blue)}.navigationItem__button_pointer_none{pointer-events:none}.navigationItem__button_disabled{background-color:var(--guit-sem-color-background-neutral-1);color:var(--guit-sem-color-foreground-disabled);pointer-events:none}.navigationItem__button_shift_1{padding-inline-start:var(--guit-ref-spacing-5xlarge)}.navigationItem__button_shift_2{padding-inline-start:var(--guit-ref-spacing-8xlarge)}.navigationItem__button_shift_3{padding-inline-start:var(--guit-ref-spacing-9xlarge)}.navigationItem__button_shift_4{padding-inline-start:var(--guit-ref-spacing-10xlarge)}.navigationItem__chevron{margin-left:auto}.navigationItem__chevron_open{transform:rotate(90deg)}";
136
172
  styleInject(css_248z);
137
173
 
174
+ const ELEMENT_HEIGHT = 62;
138
175
  const findPath = (navigationData, targetPath, currentPath = []) => {
139
176
  for (let i = 0; i < navigationData.length; i++) {
140
177
  const item = navigationData[i];
@@ -154,26 +191,25 @@ const hasDataAndChildren = (data, index) => {
154
191
  var _a, _b;
155
192
  return !!(data && index !== null && ((_a = data[index]) === null || _a === void 0 ? void 0 : _a.children) && ((_b = data[index].children) === null || _b === void 0 ? void 0 : _b.length) > 0);
156
193
  };
157
- const NavMenuContent = ({ data, depth = 0, onClick, activePathIndex }) => {
194
+ const NavMenuContent = ({ data, depth = 0, onClick, activePathIndex, render }) => {
158
195
  var _a;
159
196
  if (!data)
160
197
  return null;
161
198
  return (_a = data.children) === null || _a === void 0 ? void 0 : _a.map((item, index) => {
162
199
  const itemKey = `${item.title}-${item.path}`;
163
200
  return (React__default.createElement(Fragment, { key: itemKey },
164
- React__default.createElement(NavigationItem, { title: item.title, path: item.path, depth: depth, Icon: item.Icon, onClick: onClick, disabled: item.disabled, selected: index === (activePathIndex === null || activePathIndex === void 0 ? void 0 : activePathIndex[0]) }, item.children && (React__default.createElement(NavMenuContent, { data: item, depth: depth + 1, onClick: onClick, activePathIndex: activePathIndex === null || activePathIndex === void 0 ? void 0 : activePathIndex.slice(1) })))));
201
+ React__default.createElement(NavigationItem, { title: item.title, path: item.path, depth: depth, Icon: item.Icon, onClick: onClick, disabled: item.disabled, selected: index === (activePathIndex === null || activePathIndex === void 0 ? void 0 : activePathIndex[0]), render: render }, item.children && (React__default.createElement(NavMenuContent, { data: item, depth: depth + 1, onClick: onClick, activePathIndex: activePathIndex === null || activePathIndex === void 0 ? void 0 : activePathIndex.slice(1), render: render })))));
165
202
  });
166
203
  };
167
204
  /**
168
205
  * Navigation is a vertical component that appears on the left side of a user interface. It provides users with quick access to key features.
169
206
  */
170
- const Navigation = ({ className, open, navigationData = [], activePath, onClick, navigationCreateData, onNavigationCreateDataClick, moreMenuTitle = "More" }) => {
207
+ const Navigation = ({ className, open, navigationData = [], activePath, onClick, navigationCreateData, onNavigationCreateDataClick, moreMenuTitle = "More", render }) => {
171
208
  const [currentDataIndex, setCurrentDataIndex] = useState(null);
172
209
  const [hoverDataIndex, setHoverDataIndex] = useState(null);
173
210
  const [forceOpen, setForceOpen] = useState(false);
174
211
  const [maxVisibleItems, setMaxVisibleItems] = useState(0);
175
212
  const [activePathIndex, setActivePathIndex] = useState(null);
176
- const [moreMenuDataActiveIndex, setMoreMenuDataActiveIndex] = useState(-1);
177
213
  const [clonedNavigationData, setClonedNavigationData] = useState([]);
178
214
  const navColRef = useRef(null);
179
215
  const [propsForPopover, setPropsForPopover] = useState({});
@@ -213,11 +249,11 @@ const Navigation = ({ className, open, navigationData = [], activePath, onClick,
213
249
  setMenuData(menuSliceData);
214
250
  }, [maxVisibleItems, clonedNavigationData]);
215
251
  useEffect(() => {
216
- if (navColRef.current) {
217
- const { scrollWidth, offsetWidth } = navColRef.current;
252
+ const navColRefCurrent = navColRef.current;
253
+ if (navColRefCurrent) {
254
+ const { scrollWidth, offsetWidth } = navColRefCurrent;
218
255
  if (scrollWidth > offsetWidth) {
219
- const containerHeight = navColRef.current.offsetHeight;
220
- const ELEMENT_HEIGHT = 62;
256
+ const containerHeight = navColRefCurrent.offsetHeight;
221
257
  const maxVisibleItemsWithGap = Math.floor(containerHeight / ELEMENT_HEIGHT);
222
258
  setMaxVisibleItems(maxVisibleItemsWithGap);
223
259
  }
@@ -225,7 +261,7 @@ const Navigation = ({ className, open, navigationData = [], activePath, onClick,
225
261
  setMaxVisibleItems(clonedNavigationData.length);
226
262
  }
227
263
  }
228
- }, [navColRef.current, clonedNavigationData, height]);
264
+ }, [navColRef, clonedNavigationData, height]);
229
265
  useEffect(() => {
230
266
  const lastVisibleItemIndex = maxVisibleItems - 1;
231
267
  if (lastVisibleItemIndex === -1)
@@ -277,54 +313,49 @@ const Navigation = ({ className, open, navigationData = [], activePath, onClick,
277
313
  }
278
314
  }
279
315
  };
280
- const onMoreMenuItemsClickHandler = (item) => {
281
- const foundedItem = navigationData.find((data) => {
282
- return !Array.isArray(item.children) && item.children === data.title;
283
- });
284
- const index = maxVisibleItems + +item.id;
285
- setCurrentDataIndex(index);
286
- setForceOpen(hasDataAndChildren(clonedNavigationData, index));
316
+ const onMoreMenuItemsClickHandler = (menuItemProps) => {
317
+ const indexInMenuData = Number(menuItemProps.id);
318
+ const originalIndex = maxVisibleItems + indexInMenuData;
319
+ const clickedItem = clonedNavigationData[originalIndex];
320
+ if (!clickedItem)
321
+ return;
322
+ setCurrentDataIndex(originalIndex);
323
+ setForceOpen(hasDataAndChildren(clonedNavigationData, originalIndex));
287
324
  setHoverDataIndex(null);
288
- if ((foundedItem === null || foundedItem === void 0 ? void 0 : foundedItem.path) && onClick) {
289
- onClick(foundedItem.path);
325
+ if (clickedItem.path && onClick) {
326
+ onClick(clickedItem.path);
290
327
  }
291
- setActivePathIndex([maxVisibleItems - 1]);
292
328
  };
293
329
  const onNavigationCreateDataClickHandler = (item) => onNavigationCreateDataClick === null || onNavigationCreateDataClick === void 0 ? void 0 : onNavigationCreateDataClick(item);
294
- useEffect(() => {
295
- if (!(clonedNavigationData.length > maxVisibleItems))
296
- return;
297
- const index = activePathIndex === null || activePathIndex === void 0 ? void 0 : activePathIndex[0];
298
- if (typeof index === "number" && index >= maxVisibleItems) {
299
- setMoreMenuDataActiveIndex(maxVisibleItems - index);
300
- }
301
- else {
302
- setMoreMenuDataActiveIndex(-1);
303
- }
304
- }, [maxVisibleItems, activePathIndex]);
305
330
  const onCreateMenuClickHandler = (isOpen) => {
306
331
  setIsCreateMenuOpen(isOpen);
307
332
  };
308
- return (React__default.createElement("div", { className: classNames("navigation", className), role: "navigation" }, isMobileBreakpoint ? (
309
- // todo create navigation mobile
310
- React__default.createElement("div", null, "Mobile Navigation")) : (React__default.createElement(React__default.Fragment, null,
333
+ return (React__default.createElement("div", { className: classNames("navigation", className), role: "navigation" }, isMobileBreakpoint ? (React__default.createElement("div", null, "Mobile Navigation")) : (React__default.createElement(React__default.Fragment, null,
311
334
  React__default.createElement("nav", { className: "navigation__list" },
312
335
  React__default.createElement("div", { className: "navigation__listWrapper" },
313
- React__default.createElement("div", { className: "navigation__listItems", ref: navColRef }, clonedNavigationData === null || clonedNavigationData === void 0 ? void 0 : clonedNavigationData.map((item, index) => {
314
- return (React__default.createElement(Fragment, { key: `${item.title}-${item.path}` },
315
- React__default.createElement(NavigationColItem, { isVisible: index < maxVisibleItems, Icon: item.Icon, title: item.title, onClick: onNavigationColItemClick, index: index, path: item.path, opened: forceOpen, currentSelected: currentDataIndex === index, selected: index === (activePathIndex === null || activePathIndex === void 0 ? void 0 : activePathIndex[0]), onMouseEnter: onMouseEnterHandler, disabled: item.disabled, propsForPopover: hoverDataIndex === index ? propsForPopover : {}, hasChildren: item.children && item.children.length > 0 }),
316
- hoverDataIndex !== null && item.children && item.children.length > 0 && (React__default.createElement(Popover, { setProps: setPropsForPopover, size: "small", position: "right-top", withArrow: true, trigger: "hover", margin: 20, disableReposition: false, ref: popoverRef },
317
- React__default.createElement(PopoverBody, { withPadding: false },
318
- React__default.createElement("div", { className: "navigation__menu_wrapper" },
319
- React__default.createElement(NavMenuContent, { data: clonedNavigationData[hoverDataIndex], onClick: onItemClickHandler, activePathIndex: activePathIndex &&
320
- hoverDataIndex === activePathIndex[0]
321
- ? activePathIndex.slice(1)
322
- : null })))))));
323
- })),
336
+ React__default.createElement("div", { className: "navigation__listItems", ref: navColRef }, clonedNavigationData === null || clonedNavigationData === void 0 ? void 0 : clonedNavigationData.map((item, index) => (React__default.createElement(Fragment, { key: `${item.title}-${item.path}` },
337
+ React__default.createElement(NavigationColItem, { isVisible: index < maxVisibleItems, Icon: item.Icon, title: item.title, onClick: onNavigationColItemClick, index: index, path: item.path, opened: forceOpen, currentSelected: currentDataIndex === index, selected: index === (activePathIndex === null || activePathIndex === void 0 ? void 0 : activePathIndex[0]), onMouseEnter: onMouseEnterHandler, disabled: item.disabled, propsForPopover: hoverDataIndex === index ? propsForPopover : {}, hasChildren: item.children && item.children.length > 0, render: render }),
338
+ hoverDataIndex !== null && item.children && item.children.length > 0 && (React__default.createElement(Popover, { setProps: setPropsForPopover, size: "small", position: "right-top", withArrow: true, trigger: "hover", margin: 20, disableReposition: false, ref: popoverRef },
339
+ React__default.createElement(PopoverBody, { withPadding: false },
340
+ React__default.createElement("div", { className: "navigation__menu_wrapper" },
341
+ React__default.createElement(NavMenuContent, { data: clonedNavigationData[hoverDataIndex], onClick: onItemClickHandler, activePathIndex: activePathIndex && hoverDataIndex === activePathIndex[0]
342
+ ? activePathIndex.slice(1)
343
+ : null, render: render }))))))))),
324
344
  clonedNavigationData.length > maxVisibleItems && (React__default.createElement(React__default.Fragment, null,
325
345
  React__default.createElement(NavigationColItem, { Icon: SvgThreeDotsHorizontal, title: moreMenuTitle, isVisible: true, propsForPopover: menuPropsForPopover }),
326
- React__default.createElement(Menu, { position: "right-bottom", onChange: onMoreMenuItemsClickHandler, setPropsForPopover: setMenuPropsForPopover }, menuData.map(({ Icon, title, disabled }, index) => {
327
- return (React__default.createElement(MenuItem, { key: `menuitem-${title}-${(Icon === null || Icon === void 0 ? void 0 : Icon.displayName) || (Icon === null || Icon === void 0 ? void 0 : Icon.name) || index}`, id: index, IconBefore: Icon, selected: index === moreMenuDataActiveIndex, disabled: disabled }, title));
346
+ React__default.createElement(Menu, { position: "right-bottom", onChange: onMoreMenuItemsClickHandler, setPropsForPopover: setMenuPropsForPopover }, menuData.map((item, index) => {
347
+ const isParentItem = !!item.children && item.children.length > 0;
348
+ const menuItemRender = render && item.path && !isParentItem
349
+ ? (menuLinkData) => render({
350
+ path: item.path,
351
+ title: menuLinkData.title,
352
+ isActive: activePath === item.path,
353
+ hasChildren: isParentItem,
354
+ isDisabled: item.disabled,
355
+ Icon: item.Icon
356
+ })
357
+ : undefined;
358
+ return (React__default.createElement(MenuItem, { key: `menuitem-${item.title}-${item.path || index}`, id: index, IconBefore: item.Icon, selected: activePath === item.path, disabled: item.disabled, render: menuItemRender }, item.title));
328
359
  })))),
329
360
  navigationCreateData && (React__default.createElement(React__default.Fragment, null,
330
361
  React__default.createElement(Button, Object.assign({ onClick: () => { }, Icon: isCreateMenuOpen ? SvgX : SvgPlus, size: "large", appearance: "secondary", className: "navigation__addButton" }, propsForCreatePopover)),
@@ -339,7 +370,7 @@ const Navigation = ({ className, open, navigationData = [], activePath, onClick,
339
370
  React__default.createElement("div", { className: "navigation__menu_wrapper" },
340
371
  React__default.createElement(NavMenuContent, { data: clonedNavigationData[currentDataIndex], onClick: onItemClickHandler, activePathIndex: activePathIndex && currentDataIndex === activePathIndex[0]
341
372
  ? activePathIndex.slice(1)
342
- : null })))))))));
373
+ : null, render: render })))))))));
343
374
  };
344
375
 
345
376
  export { Navigation as default };
@@ -28,7 +28,7 @@ interface IMenuProps {
28
28
  */
29
29
  children: ReactElement | ReactElement[];
30
30
  /**
31
- * Indicates whether the menu is in a loading state. If true, a loading indicator is displayed instead of the menu items.
31
+ * Indicates whether the menu is in a loading state. If true, a loading indicator is displayed instead of the menu items.
32
32
  */
33
33
  loading?: boolean;
34
34
  /**
@@ -38,9 +38,9 @@ interface IMenuProps {
38
38
  /**
39
39
  * Callback function hat returns all provided menu item props when a menu item is selected.
40
40
  */
41
- onChange: (item: IMenuItemProps) => void;
41
+ onChange?: (item: IMenuItemProps) => void;
42
42
  /**
43
- * If true, enables swapping behavior, modifying the appearance and behavior of the menu.
43
+ * If true, enables swapping behavior, modifying the appearance and behavior of the menu.
44
44
  */
45
45
  swappable?: boolean;
46
46
  /**
@@ -54,8 +54,8 @@ interface IMenuProps {
54
54
  */
55
55
  size?: SizeType;
56
56
  /**
57
- * Position of the Menu first popover, relative to the reference (trigger, anchor) element.<br>
58
- * Possible values: `bottom-center | bottom-left | bottom-right | left-bottom | left-center` <br> `left-top | right-bottom | right-center | right-top | top-center | top-left | top-right | auto`
57
+ * Position of the Menu first popover, relative to the reference (trigger, anchor) element.<br/>
58
+ * Possible values: `bottom-center | bottom-left | bottom-right | left-bottom | left-center` <br/> `left-top | right-bottom | right-center | right-top | top-center | top-left | top-right | auto`
59
59
  */
60
60
  position?: IPopoverProps["position"];
61
61
  /**
@@ -54,7 +54,7 @@ interface IMenuItemProps {
54
54
  */
55
55
  paths?: string[];
56
56
  /**
57
- * Indicates whether the menu is in a loading state. If true, a loading indicator is displayed instead of the menu items.
57
+ * Indicates whether the menu is in a loading state. If true, a loading indicator is displayed instead of the menu items.
58
58
  */
59
59
  loading?: boolean;
60
60
  /**
@@ -62,6 +62,13 @@ interface IMenuItemProps {
62
62
  */
63
63
  loadingText?: string;
64
64
  generatedId?: string;
65
+ /**
66
+ * Custom render function for the menu item.
67
+ */
68
+ render?: (linkData: {
69
+ id: number | string;
70
+ title?: string;
71
+ }) => ReactNode;
65
72
  }
66
73
  declare const MenuItem: FC<IMenuItemProps>;
67
74
  export { IMenuItemProps, MenuItem as default };
@@ -1,4 +1,4 @@
1
- import { FC } from "react";
1
+ import { FC, ReactNode } from "react";
2
2
  import { IMenuItemProps } from "@components/molecules/Menu";
3
3
  import "./Navigation.scss";
4
4
  export interface INavigationData {
@@ -43,6 +43,17 @@ interface INavigationProps {
43
43
  * Optional custom title for the "More" menu.
44
44
  */
45
45
  moreMenuTitle?: string;
46
+ /**
47
+ * Custom render function for navigation links.
48
+ */
49
+ render?: (linkData: {
50
+ path?: string;
51
+ title?: string;
52
+ isActive?: boolean;
53
+ hasChildren?: boolean;
54
+ isDisabled?: boolean;
55
+ Icon?: FC;
56
+ }) => ReactNode;
46
57
  }
47
58
  export declare const findPath: (navigationData: INavigationData[], targetPath: string, currentPath?: number[]) => number[] | null;
48
59
  /**
@@ -1,14 +1,16 @@
1
- import React, { FC } from "react";
1
+ import { FC, ReactNode } from "react";
2
2
  import { IconProps } from "@geneui/icons";
3
+ import { INavigationProps } from "../Navigation";
3
4
  interface INavigationItemProps {
4
5
  title: string;
5
- children?: React.ReactNode;
6
+ children?: ReactNode;
6
7
  onClick?: (path: string) => void;
7
8
  disabled?: boolean;
8
9
  selected?: boolean;
9
10
  depth: number;
10
11
  Icon?: FC<IconProps>;
11
12
  path?: string;
13
+ render?: INavigationProps["render"];
12
14
  }
13
15
  declare const NavigationItem: FC<INavigationItemProps>;
14
16
  export { INavigationItemProps, NavigationItem as default };
@@ -1,5 +1,6 @@
1
1
  import { FC } from "react";
2
2
  import { IconProps } from "@geneui/icons";
3
+ import { INavigationProps } from "../Navigation";
3
4
  interface INavigationColItemProps {
4
5
  Icon?: FC<IconProps>;
5
6
  selected?: boolean;
@@ -14,6 +15,7 @@ interface INavigationColItemProps {
14
15
  isVisible?: boolean;
15
16
  hasChildren?: boolean;
16
17
  currentSelected?: boolean;
18
+ render?: INavigationProps["render"];
17
19
  }
18
20
  declare const NavigationColItem: FC<INavigationColItemProps>;
19
21
  export { INavigationColItemProps, NavigationColItem as default };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@geneui/components",
3
3
  "description": "The Gene UI components library designed for BI tools",
4
- "version": "3.0.0-next-e06ec94-05082025",
4
+ "version": "3.0.0-next-67f77e7-11082025",
5
5
  "author": "SoftConstruct",
6
6
  "homepage": "https://github.com/softconstruct/gene-ui-components#readme",
7
7
  "repository": {