@react-fabric/core 1.0.0-beta.61 → 1.0.0-beta.62

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.
Files changed (54) hide show
  1. package/css/components/_breadcrumbs.css +2 -0
  2. package/css/components/_button.css +8 -2
  3. package/css/components/_modal.css +2 -2
  4. package/css/core/_theme.css +1 -1
  5. package/css/reset/_reset.css +6 -2
  6. package/dist/cjs/components/button/Button.js +1 -1
  7. package/dist/cjs/components/dropdown/Dropdown.js +13 -1
  8. package/dist/cjs/components/icon/Icon.js +24 -24
  9. package/dist/cjs/components/menu/Menu.js +4 -3
  10. package/dist/cjs/components/menu/MenuItem.js +1 -1
  11. package/dist/cjs/components/navigator/Navigator.js +2 -2
  12. package/dist/cjs/components/tabpanel/TabPanel.js +3 -2
  13. package/dist/cjs/core/theme/ThemeProvider.js +3 -2
  14. package/dist/cjs/hooks/useNavigator.js +9 -8
  15. package/dist/cjs/hooks/useOverlayService.js +34 -13
  16. package/dist/cjs/i18n/index.js +4 -4
  17. package/dist/cjs/index.js +1 -0
  18. package/dist/esm/components/button/Button.js +1 -1
  19. package/dist/esm/components/dropdown/Dropdown.js +14 -2
  20. package/dist/esm/components/icon/Icon.js +24 -24
  21. package/dist/esm/components/menu/Menu.js +4 -3
  22. package/dist/esm/components/menu/MenuItem.js +1 -1
  23. package/dist/esm/components/navigator/Navigator.js +2 -2
  24. package/dist/esm/components/tabpanel/TabPanel.js +3 -2
  25. package/dist/esm/core/theme/ThemeProvider.js +3 -2
  26. package/dist/esm/hooks/useNavigator.js +10 -9
  27. package/dist/esm/hooks/useOverlayService.js +35 -14
  28. package/dist/esm/i18n/index.js +2 -2
  29. package/dist/esm/index.js +1 -1
  30. package/dist/types/components/index.d.ts +2 -2
  31. package/dist/types/components/menu/types.d.ts +6 -1
  32. package/dist/types/components/navigator/Navigator.d.ts +6 -0
  33. package/dist/types/core/theme/ThemeProvider.d.ts +2 -2
  34. package/dist/types/hooks/useOverlayService.d.ts +3 -5
  35. package/package.json +3 -3
  36. package/src/components/button/Button.tsx +3 -1
  37. package/src/components/dropdown/Dropdown.tsx +23 -6
  38. package/src/components/icon/Icon.tsx +22 -22
  39. package/src/components/index.ts +4 -3
  40. package/src/components/menu/Menu.tsx +3 -2
  41. package/src/components/menu/MenuItem.tsx +1 -1
  42. package/src/components/menu/types.ts +6 -1
  43. package/src/components/navigator/Navigator.tsx +9 -2
  44. package/src/components/tabpanel/TabPanel.tsx +2 -1
  45. package/src/core/theme/ThemeProvider.tsx +4 -2
  46. package/src/hooks/useNavigator.tsx +10 -11
  47. package/src/hooks/useOverlayService.tsx +29 -16
  48. package/src/i18n/index.ts +2 -2
  49. /package/dist/cjs/i18n/{ar.json.js → ar/core.json.js} +0 -0
  50. /package/dist/cjs/i18n/{en.json.js → en/core.json.js} +0 -0
  51. /package/dist/esm/i18n/{ar.json.js → ar/core.json.js} +0 -0
  52. /package/dist/esm/i18n/{en.json.js → en/core.json.js} +0 -0
  53. /package/src/i18n/{ar.json → ar/core.json} +0 -0
  54. /package/src/i18n/{en.json → en/core.json} +0 -0
@@ -1,10 +1,10 @@
1
1
  'use client';
2
2
 
3
3
  import _slicedToArray from '@babel/runtime/helpers/slicedToArray';
4
+ import { getValue } from '@react-fabric/utilities';
4
5
  import classNames from 'classnames';
5
6
  import { useState, useMemo, Children, useEffect, useCallback, cloneElement } from 'react';
6
7
  import { jsxs, jsx } from 'react/jsx-runtime';
7
- import '@react-fabric/utilities';
8
8
  import { nodeCheck } from '../../utils/nodeCheck.js';
9
9
  import { Tab } from './Tab.js';
10
10
 
@@ -73,7 +73,8 @@ var TabPanel = function TabPanel(_ref) {
73
73
  tabs = _useMemo2[0],
74
74
  rest = _useMemo2[1];
75
75
  useEffect(function () {
76
- setActive(activeTab);
76
+ var _tabs$;
77
+ setActive(getValue(activeTab !== null && activeTab !== void 0 ? activeTab : (_tabs$ = tabs[0]) === null || _tabs$ === void 0 ? void 0 : _tabs$.props.id));
77
78
  }, [activeTab]);
78
79
  var activeTabPanel = useMemo(function () {
79
80
  var _tabs$find;
@@ -59,7 +59,8 @@ var ThemeProvider = function ThemeProvider(_ref) {
59
59
  accentColor = _ref.accentColor,
60
60
  tintColor = _ref.tintColor,
61
61
  colorScheme = _ref.colorScheme,
62
- rounding = _ref.rounding;
62
+ rounding = _ref.rounding,
63
+ className = _ref.className;
63
64
  var _useApplicationContex = useApplicationContext(),
64
65
  currentColorScheme = _useApplicationContex.currentColorScheme;
65
66
  var styles = useMemo(function () {
@@ -88,7 +89,7 @@ var ThemeProvider = function ThemeProvider(_ref) {
88
89
  style: styles,
89
90
  "data-color-scheme": colorScheme !== null && colorScheme !== void 0 ? colorScheme : currentColorScheme,
90
91
  "data-rounding": rounding,
91
- className: classNames("fabric-themeProvider", "contents theme-base", branding),
92
+ className: classNames("fabric-themeProvider", "contents theme-base", branding, className),
92
93
  children: children
93
94
  });
94
95
  };
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import _slicedToArray from '@babel/runtime/helpers/slicedToArray';
4
- import { useState, useRef, useCallback, useMemo } from 'react';
4
+ import { useState, useCallback, useMemo } from 'react';
5
5
  import { jsxs } from 'react/jsx-runtime';
6
6
 
7
7
  var useNavigator = function useNavigator() {
@@ -11,15 +11,16 @@ var useNavigator = function useNavigator() {
11
11
  _useState2 = _slicedToArray(_useState, 2),
12
12
  currentIndex = _useState2[0],
13
13
  setCurrentIndex = _useState2[1];
14
- var refCurrent = useRef(defaultIndex);
15
14
  var onNavigate = useCallback(function (dir) {
16
- if (dir === -1) {
17
- refCurrent.current = refCurrent.current > 0 ? refCurrent.current - 1 : totalCount - 1;
18
- }
19
- if (dir === 1) {
20
- refCurrent.current = refCurrent.current + 1 < totalCount ? refCurrent.current + 1 : 0;
21
- }
22
- setCurrentIndex(refCurrent.current);
15
+ setCurrentIndex(function (prevValue) {
16
+ if (dir === -1) {
17
+ return prevValue > 0 ? prevValue - 1 : totalCount - 1;
18
+ }
19
+ if (dir === 1) {
20
+ return prevValue + 1 < totalCount ? prevValue + 1 : 0;
21
+ }
22
+ return prevValue;
23
+ });
23
24
  }, [totalCount]);
24
25
  var headLabel = useMemo(function () {
25
26
  return /*#__PURE__*/jsxs("span", {
@@ -5,12 +5,19 @@ import _objectWithoutProperties from '@babel/runtime/helpers/objectWithoutProper
5
5
  import _asyncToGenerator from '@babel/runtime/helpers/asyncToGenerator';
6
6
  import _slicedToArray from '@babel/runtime/helpers/slicedToArray';
7
7
  import _regeneratorRuntime from '@babel/runtime/regenerator';
8
- import { useState } from 'react';
8
+ import { useState, useRef } from 'react';
9
9
  import { jsx } from 'react/jsx-runtime';
10
10
 
11
11
  var _excluded = ["onClose"];
12
12
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
13
13
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
14
+ // return the blank function since state functionality will execute if the value is function
15
+ var EMPTY_FC = function EMPTY_FC() {
16
+ return function () {
17
+ return null;
18
+ };
19
+ };
20
+
14
21
  /**
15
22
  * Hook to manage overlay components like modals or flyouts.
16
23
  * It provides a way to open an overlay with specified properties and handle its closure.
@@ -21,7 +28,8 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
21
28
  * of the overlay when it is closed, allowing you to handle the result in a controlled manner.
22
29
  *
23
30
  * @param ModalOrFlyout - The component to be used as an overlay.
24
- * @returns An array containing the current overlay and a function to open it.
31
+ * @param toggleOpen - If true, calling `openOverlay` while an overlay is already open will close the existing one instead of opening a new one.
32
+ * @returns An array containing the current overlay, a function to open it, and a boolean indicating if the overlay is open.
25
33
  *
26
34
  * @example
27
35
  * ```jsx
@@ -36,34 +44,47 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
36
44
  * ```
37
45
  */
38
46
  var useOverlayService = function useOverlayService(ModalOrFlyout) {
39
- var _useState = useState(null),
47
+ var _useState = useState(EMPTY_FC),
40
48
  _useState2 = _slicedToArray(_useState, 2),
41
49
  Overlay = _useState2[0],
42
50
  setOverlay = _useState2[1];
51
+ var existingOpenPromise = useRef(null);
43
52
  var openOverlay = /*#__PURE__*/function () {
44
53
  var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
45
54
  var _ref2,
46
55
  onClose,
47
- props,
56
+ outerProps,
48
57
  _args = arguments;
49
58
  return _regeneratorRuntime.wrap(function _callee$(_context) {
50
59
  while (1) switch (_context.prev = _context.next) {
51
60
  case 0:
52
- _ref2 = _args.length > 0 && _args[0] !== undefined ? _args[0] : {}, onClose = _ref2.onClose, props = _objectWithoutProperties(_ref2, _excluded);
53
- _context.next = 3;
61
+ _ref2 = _args.length > 0 && _args[0] !== undefined ? _args[0] : {}, onClose = _ref2.onClose, outerProps = _objectWithoutProperties(_ref2, _excluded);
62
+ if (!existingOpenPromise.current) {
63
+ _context.next = 3;
64
+ break;
65
+ }
66
+ return _context.abrupt("return");
67
+ case 3:
68
+ _context.next = 5;
54
69
  return new Promise(function (resolve) {
55
- var handleClose = function handleClose(args) {
56
- setOverlay(null);
70
+ existingOpenPromise.current = function (args) {
71
+ setOverlay(EMPTY_FC);
57
72
  resolve(args);
58
73
  onClose === null || onClose === void 0 || onClose(args);
74
+ existingOpenPromise.current = null;
59
75
  };
60
- setOverlay( /*#__PURE__*/jsx(ModalOrFlyout, _objectSpread(_objectSpread({}, props), {}, {
61
- onClose: handleClose
62
- })));
76
+ // eslint-disable-next-line react/display-name
77
+ setOverlay(function () {
78
+ return function (overrideProps) {
79
+ return /*#__PURE__*/jsx(ModalOrFlyout, _objectSpread(_objectSpread(_objectSpread({}, outerProps), overrideProps), {}, {
80
+ onClose: existingOpenPromise.current
81
+ }));
82
+ };
83
+ });
63
84
  });
64
- case 3:
85
+ case 5:
65
86
  return _context.abrupt("return", _context.sent);
66
- case 4:
87
+ case 6:
67
88
  case "end":
68
89
  return _context.stop();
69
90
  }
@@ -73,7 +94,7 @@ var useOverlayService = function useOverlayService(ModalOrFlyout) {
73
94
  return _ref.apply(this, arguments);
74
95
  };
75
96
  }();
76
- return [Overlay, openOverlay];
97
+ return [Overlay, openOverlay, existingOpenPromise.current];
77
98
  };
78
99
 
79
100
  export { useOverlayService };
@@ -1,8 +1,8 @@
1
1
  'use client';
2
2
 
3
3
  import i18next from 'i18next';
4
- import ar from './ar.json.js';
5
- import en from './en.json.js';
4
+ import ar from './ar/core.json.js';
5
+ import en from './en/core.json.js';
6
6
 
7
7
  /*
8
8
  * React Fabric
package/dist/esm/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  export { ActionLabel } from './components/actionLabel/ActionLabel.js';
4
- export { AnimationIndicator, LoadingBars, LoadingLine, LoadingSpinner, Skeleton } from './components/animations/Animations.js';
4
+ export { AnimationIndicator, AnimationLoader, LoadingBars, LoadingLine, LoadingSpinner, Skeleton } from './components/animations/Animations.js';
5
5
  export { Avatar } from './components/avatar/Avatar.js';
6
6
  export { AvatarGroup } from './components/avatar/AvatarGroup.js';
7
7
  export { Badge, getBadgeProps } from './components/badge/Badge.js';
@@ -1,5 +1,5 @@
1
1
  export { ActionLabel } from "./actionLabel/ActionLabel";
2
- export { LoadingBars, AnimationIndicator, LoadingSpinner, LoadingLine, Skeleton, } from "./animations/Animations";
2
+ export { AnimationIndicator, AnimationLoader, LoadingBars, LoadingLine, LoadingSpinner, Skeleton, } from "./animations/Animations";
3
3
  export { Avatar } from "./avatar/Avatar";
4
4
  export { AvatarGroup } from "./avatar/AvatarGroup";
5
5
  export { Badge, getBadgeProps } from "./badge/Badge";
@@ -18,7 +18,7 @@ export { Divider } from "./divider/Divider";
18
18
  export { Dropdown, DropdownDismiss } from "./dropdown/Dropdown";
19
19
  export { DropdownTool } from "./dropdown/DropdownTool";
20
20
  export { EmptyContent } from "./empty/EmptyContent";
21
- export { Icon, getIconProps } from "./icon/Icon";
21
+ export { getIconProps, Icon } from "./icon/Icon";
22
22
  export { Image, Video } from "./media/Media";
23
23
  export { ContextMenu } from "./menu/ContextMenu";
24
24
  export { Menu } from "./menu/Menu";
@@ -13,7 +13,7 @@ export interface MenuItemProps extends CssProp, AriaProps, TestProps {
13
13
  /**
14
14
  * append label badge text
15
15
  */
16
- appendLabel?: string | false;
16
+ appendLabel?: JSX.Element | string | number | false;
17
17
  /**
18
18
  * menu color
19
19
  */
@@ -50,6 +50,10 @@ export interface MenuItemProps extends CssProp, AriaProps, TestProps {
50
50
  export interface MenuRootProps extends CssProp {
51
51
  label?: never;
52
52
  minimal?: boolean;
53
+ /**
54
+ * use this create a dropdown button within menu-item
55
+ */
56
+ forDropdown?: boolean;
53
57
  menuClassName?: never;
54
58
  onClick?: (id: string) => CallbackReturn;
55
59
  }
@@ -57,6 +61,7 @@ export interface MenuInnerProps extends MenuItemProps {
57
61
  label: string;
58
62
  minimal?: never;
59
63
  trigger?: "hover" | "click";
64
+ forDropdown?: boolean;
60
65
  /**
61
66
  * floating menu className
62
67
  */
@@ -1,6 +1,12 @@
1
1
  import { type AriaProps, type ChildProp, type ColorType, type CssProp, type PaletteType, type TestProps } from "../../types";
2
2
  export interface NavigatorProps extends CssProp, AriaProps, TestProps, Partial<ChildProp> {
3
+ /**
4
+ * item list length (ignore to enable infinite navigation)
5
+ */
3
6
  length?: number;
7
+ /**
8
+ * current item index (ignore to enable infinite navigation)
9
+ */
4
10
  current?: number;
5
11
  disableKeyHandlers?: boolean;
6
12
  /**
@@ -1,4 +1,4 @@
1
- import { type ChildrenProp } from "../../types";
1
+ import { type ChildrenProp, type CssProp } from "../../types";
2
2
  export interface ThemeProps {
3
3
  /**
4
4
  * light/dark color scheme
@@ -54,4 +54,4 @@ export interface ThemeProps {
54
54
  *
55
55
  * @see {@link https://adarshpastakia.github.io/react-fabric/?path=/story/core-application--theme-provider} for more details.
56
56
  */
57
- export declare const ThemeProvider: ({ children, branding, primaryColor, accentColor, tintColor, colorScheme, rounding, }: ThemeProps & ChildrenProp) => import("react/jsx-runtime").JSX.Element;
57
+ export declare const ThemeProvider: ({ children, branding, primaryColor, accentColor, tintColor, colorScheme, rounding, className, }: ThemeProps & ChildrenProp & CssProp) => import("react/jsx-runtime").JSX.Element;
@@ -1,6 +1,4 @@
1
- import { type ElementType } from "react";
2
1
  import { type ModalProps } from "../types";
3
- type OverlayComponent = ElementType<ModalProps<AnyObject>>;
4
2
  /**
5
3
  * Hook to manage overlay components like modals or flyouts.
6
4
  * It provides a way to open an overlay with specified properties and handle its closure.
@@ -11,7 +9,8 @@ type OverlayComponent = ElementType<ModalProps<AnyObject>>;
11
9
  * of the overlay when it is closed, allowing you to handle the result in a controlled manner.
12
10
  *
13
11
  * @param ModalOrFlyout - The component to be used as an overlay.
14
- * @returns An array containing the current overlay and a function to open it.
12
+ * @param toggleOpen - If true, calling `openOverlay` while an overlay is already open will close the existing one instead of opening a new one.
13
+ * @returns An array containing the current overlay, a function to open it, and a boolean indicating if the overlay is open.
15
14
  *
16
15
  * @example
17
16
  * ```jsx
@@ -25,5 +24,4 @@ type OverlayComponent = ElementType<ModalProps<AnyObject>>;
25
24
  * <Overlay />
26
25
  * ```
27
26
  */
28
- export declare const useOverlayService: (ModalOrFlyout: OverlayComponent) => [Overlay: React.ReactNode | null, openOverlay: (props?: KeyValue) => Promise<AnyObject>];
29
- export {};
27
+ export declare const useOverlayService: <T extends AnyObject = AnyObject>(ModalOrFlyout: React.FunctionComponent<ModalProps<T>>) => [Overlay: React.FunctionComponent<Partial<T>>, openOverlay: (props?: Partial<T>) => Promise<AnyObject>, closeOverlay: ((args?: AnyObject) => void) | null];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-fabric/core",
3
- "version": "1.0.0-beta.61",
3
+ "version": "1.0.0-beta.62",
4
4
  "description": "RUF: Core package",
5
5
  "author": "Adarsh Pastakia <adarshpastakia@outlook.com>",
6
6
  "license": "MIT",
@@ -39,7 +39,7 @@
39
39
  "@iconify-json/mdi": "~1.2.3",
40
40
  "@iconify-json/svg-spinners": "~1.2.4",
41
41
  "@iconify/tailwind4": "~1.2.1",
42
- "@react-fabric/utilities": "^1.0.0-beta.61",
42
+ "@react-fabric/utilities": "^1.0.0-beta.62",
43
43
  "@tailwindcss/postcss": "~4.1.16",
44
44
  "boring-avatars": "~1.11.2",
45
45
  "classnames": "~2.5.1",
@@ -57,5 +57,5 @@
57
57
  "node": ">=18.12.0",
58
58
  "npm": ">=8.19.0"
59
59
  },
60
- "gitHead": "a0b0c571bf9c189fa3355bbc3b896ecc4b9d6a04"
60
+ "gitHead": "9dce2fd9bb753d67e3f8adbf50be257b4bc64242"
61
61
  }
@@ -292,7 +292,9 @@ export const Button = <Tag extends React.ElementType = "button">({
292
292
  data-loading={busy || loading}
293
293
  data-disabled={disabled}
294
294
  >
295
- {hotKey && <HotKey keyCombo={hotKey} handler={hotKeyHandler.current} />}
295
+ {!disabled && hotKey && (
296
+ <HotKey keyCombo={hotKey} handler={hotKeyHandler.current} />
297
+ )}
296
298
  <TooltipWrapper {...tooltipProps}>
297
299
  <E
298
300
  ref={mergeRefs(ref, refEl)}
@@ -36,6 +36,7 @@ import {
36
36
  useClick,
37
37
  useDismiss,
38
38
  useFloating,
39
+ useFloatingNodeId,
39
40
  useFloatingTree,
40
41
  useHover,
41
42
  useInteractions,
@@ -116,6 +117,7 @@ const DropdownElement = ({
116
117
  const arrowRef = useRef(null);
117
118
  const [isOpen, setIsOpen] = useState(false);
118
119
  const tree = useFloatingTree();
120
+ const nodeId = useFloatingNodeId();
119
121
 
120
122
  const [anchor, panel] = useMemo<AnyObject[]>(
121
123
  () => Children.toArray(children as AnyObject),
@@ -123,6 +125,7 @@ const DropdownElement = ({
123
125
  );
124
126
 
125
127
  const { refs, floatingStyles, context } = useFloating({
128
+ nodeId: `dropdown-${nodeId}`,
126
129
  open: isOpen,
127
130
  onOpenChange: (open, _, reason) => {
128
131
  if (reason === "reference-press") return;
@@ -148,6 +151,7 @@ const DropdownElement = ({
148
151
  enabled: !disabled,
149
152
  handleClose: safePolygon({ blockPointerEvents: true }),
150
153
  });
154
+
151
155
  const dismiss = useDismiss(context, {
152
156
  referencePress: true,
153
157
  });
@@ -168,6 +172,11 @@ const DropdownElement = ({
168
172
  return undefined;
169
173
  }, [refs, isOpen, fitToParent]);
170
174
 
175
+ const dismissDropdown = useCallback(() => {
176
+ setIsOpen(false);
177
+ onClose?.();
178
+ }, []);
179
+
171
180
  const tryClosing = useCallback(
172
181
  (e: React.MouseEvent) => {
173
182
  if (
@@ -259,7 +268,9 @@ const DropdownElement = ({
259
268
  )}
260
269
  onMouseUpCapture={tryClosing}
261
270
  >
262
- {panel}
271
+ {cloneElement(panel, {
272
+ dismiss: dismissDropdown,
273
+ })}
263
274
  </div>
264
275
  {showArrow && (
265
276
  <FloatingArrow
@@ -308,11 +319,17 @@ const DropdownElement = ({
308
319
  * @see {@link https://adarshpastakia.github.io/react-fabric/?path=/docs/core-components-dropdown--docs} for more details.
309
320
  * @see {@link https://floating-ui.com/docs/react} for more details on Floating UI.
310
321
  */
311
- export const Dropdown = (props: DropdownProps) => (
312
- <FloatingTree>
313
- <DropdownElement {...props} />
314
- </FloatingTree>
315
- );
322
+ export const Dropdown = (props: DropdownProps) => {
323
+ const tree = useFloatingTree();
324
+ if (tree) {
325
+ return <DropdownElement {...props} />;
326
+ }
327
+ return (
328
+ <FloatingTree>
329
+ <DropdownElement {...props} />
330
+ </FloatingTree>
331
+ );
332
+ };
316
333
 
317
334
  /**
318
335
  * A component that allows dismissing the dropdown when clicked.
@@ -191,29 +191,29 @@ export const Icon = ({
191
191
  /** ***************** render icon *******************/
192
192
  const iconEl = useMemo(() => {
193
193
  if (isValidElement(icon)) return icon;
194
- if (!isString(icon)) {
195
- throw Error("Invalid icon expected string");
194
+ if (isString(icon)) {
195
+ return isSvg ? (
196
+ <svg viewBox={viewBox}>
197
+ <path fill="currentColor" d={icon.toString()} />
198
+ </svg>
199
+ ) : icon?.toString().length <= 4 ? (
200
+ <svg role="img">
201
+ <text
202
+ x="50%"
203
+ y="50%"
204
+ dy=".1em"
205
+ dominantBaseline="middle"
206
+ textAnchor="middle"
207
+ style={{ fontSize: SvgTextSize[icon.length] }}
208
+ >
209
+ {icon}
210
+ </text>
211
+ </svg>
212
+ ) : (
213
+ <i className={`${icon}`} />
214
+ );
196
215
  }
197
- return isSvg ? (
198
- <svg viewBox={viewBox}>
199
- <path fill="currentColor" d={icon.toString()} />
200
- </svg>
201
- ) : icon?.toString().length <= 4 ? (
202
- <svg role="img">
203
- <text
204
- x="50%"
205
- y="50%"
206
- dy=".1em"
207
- dominantBaseline="middle"
208
- textAnchor="middle"
209
- style={{ fontSize: SvgTextSize[icon.length] }}
210
- >
211
- {icon}
212
- </text>
213
- </svg>
214
- ) : (
215
- <i className={`${icon}`} />
216
- );
216
+ throw Error("Invalid icon expected string");
217
217
  }, [icon, isSvg, viewBox]);
218
218
 
219
219
  return (
@@ -23,10 +23,11 @@
23
23
 
24
24
  export { ActionLabel } from "./actionLabel/ActionLabel";
25
25
  export {
26
- LoadingBars,
27
26
  AnimationIndicator,
28
- LoadingSpinner,
27
+ AnimationLoader,
28
+ LoadingBars,
29
29
  LoadingLine,
30
+ LoadingSpinner,
30
31
  Skeleton,
31
32
  } from "./animations/Animations";
32
33
  export { Avatar } from "./avatar/Avatar";
@@ -47,7 +48,7 @@ export { Divider } from "./divider/Divider";
47
48
  export { Dropdown, DropdownDismiss } from "./dropdown/Dropdown";
48
49
  export { DropdownTool } from "./dropdown/DropdownTool";
49
50
  export { EmptyContent } from "./empty/EmptyContent";
50
- export { Icon, getIconProps } from "./icon/Icon";
51
+ export { getIconProps, Icon } from "./icon/Icon";
51
52
  export { Image, Video } from "./media/Media";
52
53
  export { ContextMenu } from "./menu/ContextMenu";
53
54
  export { Menu } from "./menu/Menu";
@@ -71,6 +71,7 @@ const MenuComponent = ({
71
71
  trigger = "hover",
72
72
  // @ts-expect-error ignore
73
73
  ref,
74
+ forDropdown,
74
75
  ...rest
75
76
  }: Partial<MenuProps>) => {
76
77
  const [isOpen, setIsOpen] = useState(false);
@@ -83,10 +84,10 @@ const MenuComponent = ({
83
84
  const nodeId = useFloatingNodeId();
84
85
  const parentId = useFloatingParentNodeId();
85
86
 
86
- const isNested = parentId !== null;
87
+ const isNested = parentId !== null && !forDropdown;
87
88
 
88
89
  const { floatingStyles, refs, context } = useFloating<HTMLButtonElement>({
89
- nodeId,
90
+ nodeId: `menu-${nodeId}`,
90
91
  open: !isNested || isOpen,
91
92
  onOpenChange(open, event, reason) {
92
93
  isNested && setIsOpen(open);
@@ -177,7 +177,7 @@ export const MenuItem = <Tag extends React.ElementType = "button">({
177
177
  }}
178
178
  {...(minimal ? {} : aria)}
179
179
  >
180
- {hotKey && (
180
+ {!disabled && hotKey && (
181
181
  <HotKey keyCombo={hotKey} handler={() => elRef.current?.click?.()} />
182
182
  )}
183
183
  {menuLabel}
@@ -45,7 +45,7 @@ export interface MenuItemProps extends CssProp, AriaProps, TestProps {
45
45
  /**
46
46
  * append label badge text
47
47
  */
48
- appendLabel?: string | false;
48
+ appendLabel?: JSX.Element | string | number | false;
49
49
  /**
50
50
  * menu color
51
51
  */
@@ -83,6 +83,10 @@ export interface MenuItemProps extends CssProp, AriaProps, TestProps {
83
83
  export interface MenuRootProps extends CssProp {
84
84
  label?: never;
85
85
  minimal?: boolean;
86
+ /**
87
+ * use this create a dropdown button within menu-item
88
+ */
89
+ forDropdown?: boolean;
86
90
  menuClassName?: never;
87
91
  onClick?: (id: string) => CallbackReturn;
88
92
  }
@@ -91,6 +95,7 @@ export interface MenuInnerProps extends MenuItemProps {
91
95
  label: string;
92
96
  minimal?: never;
93
97
  trigger?: "hover" | "click";
98
+ forDropdown?: boolean;
94
99
  /**
95
100
  * floating menu className
96
101
  */
@@ -21,6 +21,7 @@
21
21
  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
  */
23
23
 
24
+ import { isEmpty } from "@react-fabric/utilities";
24
25
  import classNames from "classnames";
25
26
  import { useCallback } from "react";
26
27
  import { HotKey } from "../../hotkeys/HotKey";
@@ -33,15 +34,21 @@ import {
33
34
  type TestProps,
34
35
  } from "../../types";
35
36
  import { Icon } from "../icon/Icon";
36
- import { isEmpty } from "@react-fabric/utilities";
37
37
 
38
38
  export interface NavigatorProps
39
39
  extends CssProp,
40
40
  AriaProps,
41
41
  TestProps,
42
42
  Partial<ChildProp> {
43
+ /**
44
+ * item list length (ignore to enable infinite navigation)
45
+ */
43
46
  length?: number;
47
+ /**
48
+ * current item index (ignore to enable infinite navigation)
49
+ */
44
50
  current?: number;
51
+
45
52
  disableKeyHandlers?: boolean;
46
53
  /**
47
54
  * navigator button color
@@ -87,7 +94,7 @@ export const Navigator = ({
87
94
  }: NavigatorProps) => {
88
95
  const navPrev = useCallback(
89
96
  () => (isEmpty(current) || current !== 0) && onNavigate(-1),
90
- [onNavigate, current],
97
+ [onNavigate, current, length],
91
98
  );
92
99
  const navNext = useCallback(
93
100
  () => (isEmpty(current) || current + 1 < (length ?? 0)) && onNavigate(+1),
@@ -21,6 +21,7 @@
21
21
  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
  */
23
23
 
24
+ import { getValue } from "@react-fabric/utilities";
24
25
  import classNames from "classnames";
25
26
  import {
26
27
  Children,
@@ -153,7 +154,7 @@ export const TabPanel = ({
153
154
  }, [children]);
154
155
 
155
156
  useEffect(() => {
156
- setActive(activeTab);
157
+ setActive(getValue(activeTab ?? tabs[0]?.props.id));
157
158
  }, [activeTab]);
158
159
 
159
160
  const activeTabPanel = useMemo(() => {
@@ -24,8 +24,8 @@
24
24
  import { isColor } from "@react-fabric/utilities";
25
25
  import classNames from "classnames";
26
26
  import { useMemo } from "react";
27
- import { type ChildrenProp } from "../../types";
28
27
  import { useApplicationContext } from "../../context/Global";
28
+ import { type ChildrenProp, type CssProp } from "../../types";
29
29
 
30
30
  export interface ThemeProps {
31
31
  /**
@@ -91,7 +91,8 @@ export const ThemeProvider = ({
91
91
  tintColor,
92
92
  colorScheme,
93
93
  rounding,
94
- }: ThemeProps & ChildrenProp) => {
94
+ className,
95
+ }: ThemeProps & ChildrenProp & CssProp) => {
95
96
  const { currentColorScheme } = useApplicationContext();
96
97
  const styles = useMemo(() => {
97
98
  const ret: KeyValue = {};
@@ -125,6 +126,7 @@ export const ThemeProvider = ({
125
126
  "fabric-themeProvider",
126
127
  "contents theme-base",
127
128
  branding,
129
+ className,
128
130
  )}
129
131
  >
130
132
  {children}