@mezzanine-ui/react 1.0.0-beta.1 → 1.0.0-beta.3

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 (172) hide show
  1. package/Anchor/Anchor.d.ts +51 -18
  2. package/Anchor/Anchor.js +15 -15
  3. package/Anchor/AnchorGroup.d.ts +34 -0
  4. package/Anchor/AnchorGroup.js +37 -0
  5. package/Anchor/AnchorItem.d.ts +30 -0
  6. package/Anchor/AnchorItem.js +65 -0
  7. package/Anchor/index.d.ts +2 -0
  8. package/Anchor/index.js +1 -0
  9. package/Anchor/utils.d.ts +13 -0
  10. package/Anchor/utils.js +95 -0
  11. package/AutoComplete/AutoComplete.d.ts +194 -0
  12. package/AutoComplete/AutoComplete.js +419 -0
  13. package/AutoComplete/index.d.ts +2 -0
  14. package/AutoComplete/index.js +1 -0
  15. package/AutoComplete/useAutoCompleteCreation.d.ts +33 -0
  16. package/AutoComplete/useAutoCompleteCreation.js +201 -0
  17. package/AutoComplete/useAutoCompleteKeyboard.d.ts +31 -0
  18. package/AutoComplete/useAutoCompleteKeyboard.js +149 -0
  19. package/AutoComplete/useAutoCompleteSearch.d.ts +16 -0
  20. package/AutoComplete/useAutoCompleteSearch.js +69 -0
  21. package/AutoComplete/useCreationTracker.d.ts +17 -0
  22. package/AutoComplete/useCreationTracker.js +47 -0
  23. package/Badge/Badge.js +2 -2
  24. package/Breadcrumb/BreadcrumbItem.d.ts +1 -1
  25. package/Button/Button.js +13 -11
  26. package/Button/index.d.ts +1 -1
  27. package/Button/typings.d.ts +27 -4
  28. package/Description/Description.d.ts +30 -0
  29. package/Description/Description.js +13 -0
  30. package/Description/DescriptionContent.d.ts +41 -0
  31. package/Description/DescriptionContent.js +14 -0
  32. package/Description/DescriptionGroup.d.ts +13 -0
  33. package/Description/DescriptionGroup.js +12 -0
  34. package/Description/DescriptionTitle.d.ts +45 -0
  35. package/Description/DescriptionTitle.js +17 -0
  36. package/Description/index.d.ts +8 -0
  37. package/Description/index.js +4 -0
  38. package/Dropdown/Dropdown.d.ts +43 -3
  39. package/Dropdown/Dropdown.js +154 -35
  40. package/Dropdown/DropdownAction.d.ts +1 -1
  41. package/Dropdown/DropdownAction.js +1 -4
  42. package/Dropdown/DropdownItem.d.ts +21 -4
  43. package/Dropdown/DropdownItem.js +23 -10
  44. package/Dropdown/DropdownItemCard.d.ts +5 -5
  45. package/Dropdown/DropdownItemCard.js +11 -10
  46. package/Dropdown/DropdownStatus.d.ts +2 -2
  47. package/Dropdown/DropdownStatus.js +29 -0
  48. package/Dropdown/dropdownKeydownHandler.d.ts +2 -1
  49. package/Dropdown/dropdownKeydownHandler.js +73 -0
  50. package/Dropdown/highlightText.js +5 -1
  51. package/Dropdown/shortcutTextHandler.d.ts +24 -0
  52. package/Dropdown/shortcutTextHandler.js +171 -0
  53. package/Form/FormControlContext.d.ts +2 -2
  54. package/Form/FormField.d.ts +56 -4
  55. package/Form/FormField.js +10 -6
  56. package/Form/FormHintText.d.ts +24 -1
  57. package/Form/FormHintText.js +4 -4
  58. package/Form/FormLabel.d.ts +6 -3
  59. package/Form/FormLabel.js +5 -3
  60. package/Input/Input.d.ts +29 -3
  61. package/Input/Input.js +22 -6
  62. package/Input/PasswordStrengthIndicator/PasswordStrengthIndicator.js +1 -1
  63. package/Modal/Modal.d.ts +103 -11
  64. package/Modal/Modal.js +14 -9
  65. package/Modal/ModalBodyForVerification.d.ts +59 -0
  66. package/Modal/ModalBodyForVerification.js +99 -0
  67. package/Modal/ModalControl.d.ts +2 -2
  68. package/Modal/ModalControl.js +1 -1
  69. package/Modal/ModalFooter.d.ts +119 -1
  70. package/Modal/ModalFooter.js +15 -3
  71. package/Modal/ModalHeader.d.ts +26 -7
  72. package/Modal/ModalHeader.js +33 -7
  73. package/Modal/index.d.ts +4 -5
  74. package/Modal/index.js +1 -2
  75. package/Modal/useModalContainer.d.ts +12 -3
  76. package/Modal/useModalContainer.js +28 -6
  77. package/Navigation/CollapsedMenu.d.ts +6 -0
  78. package/Navigation/CollapsedMenu.js +16 -0
  79. package/Navigation/Navigation.d.ts +17 -3
  80. package/Navigation/Navigation.js +48 -33
  81. package/Navigation/NavigationFooter.js +4 -2
  82. package/Navigation/NavigationHeader.d.ts +11 -1
  83. package/Navigation/NavigationHeader.js +6 -3
  84. package/Navigation/NavigationOption.d.ts +3 -2
  85. package/Navigation/NavigationOption.js +45 -26
  86. package/Navigation/NavigationOptionCategory.js +20 -2
  87. package/Navigation/context.d.ts +2 -0
  88. package/Navigation/useVisibleItems.d.ts +5 -0
  89. package/Navigation/useVisibleItems.js +54 -0
  90. package/NotificationCenter/NotificationCenter.d.ts +124 -0
  91. package/NotificationCenter/NotificationCenter.js +259 -0
  92. package/NotificationCenter/NotificationCenterDrawer.d.ts +89 -0
  93. package/NotificationCenter/index.d.ts +3 -0
  94. package/NotificationCenter/index.js +1 -0
  95. package/PageFooter/PageFooter.d.ts +19 -9
  96. package/PageFooter/PageFooter.js +10 -10
  97. package/PageHeader/PageHeader.js +4 -12
  98. package/PageToolbar/PageToolbar.d.ts +2 -6
  99. package/PageToolbar/utils.js +4 -12
  100. package/Select/index.d.ts +0 -2
  101. package/Select/index.js +0 -1
  102. package/Slider/useSlider.js +1 -1
  103. package/Table/Table.d.ts +53 -15
  104. package/Table/Table.js +178 -82
  105. package/Table/TableContext.d.ts +18 -42
  106. package/Table/components/TableActionsCell.d.ts +26 -0
  107. package/Table/components/TableActionsCell.js +78 -0
  108. package/Table/components/TableBody.d.ts +2 -5
  109. package/Table/components/TableBody.js +16 -19
  110. package/Table/components/TableBulkActions.d.ts +15 -0
  111. package/Table/components/TableBulkActions.js +26 -0
  112. package/Table/components/TableCell.d.ts +2 -0
  113. package/Table/components/TableCell.js +42 -10
  114. package/Table/components/TableColGroup.js +10 -112
  115. package/Table/components/TableColumnTitleMenu.d.ts +6 -0
  116. package/Table/components/TableColumnTitleMenu.js +20 -0
  117. package/Table/components/TableDragHandleCell.d.ts +2 -0
  118. package/Table/components/TableDragHandleCell.js +8 -1
  119. package/Table/components/TableExpandCell.d.ts +2 -0
  120. package/Table/components/TableExpandCell.js +8 -1
  121. package/Table/components/TableExpandedRow.js +3 -2
  122. package/Table/components/TableHeader.d.ts +2 -4
  123. package/Table/components/TableHeader.js +11 -14
  124. package/Table/components/TableResizeHandle.js +3 -7
  125. package/Table/components/TableRow.js +54 -20
  126. package/Table/components/TableSelectionCell.d.ts +5 -0
  127. package/Table/components/TableSelectionCell.js +12 -1
  128. package/Table/components/index.d.ts +1 -0
  129. package/Table/components/index.js +1 -0
  130. package/Table/hooks/index.d.ts +1 -1
  131. package/Table/hooks/index.js +1 -1
  132. package/Table/hooks/useTableDataSource.d.ts +2 -2
  133. package/Table/hooks/useTableExpansion.js +0 -6
  134. package/Table/hooks/useTableFixedOffsets.d.ts +1 -1
  135. package/Table/hooks/useTableFixedOffsets.js +24 -26
  136. package/Table/hooks/useTableResizedColumns.d.ts +2 -0
  137. package/Table/hooks/useTableResizedColumns.js +22 -0
  138. package/Table/hooks/useTableScroll.d.ts +3 -1
  139. package/Table/hooks/useTableScroll.js +25 -19
  140. package/Table/hooks/useTableSelection.js +32 -8
  141. package/Table/hooks/useTableVirtualization.d.ts +1 -1
  142. package/Table/index.d.ts +4 -4
  143. package/Table/index.js +5 -3
  144. package/Table/utils/calculateColumnWidths.d.ts +28 -0
  145. package/Table/utils/calculateColumnWidths.js +80 -0
  146. package/Table/utils/index.d.ts +2 -0
  147. package/Table/utils/index.js +1 -0
  148. package/Table/utils/useTableRowSelection.d.ts +5 -5
  149. package/Table/utils/useTableRowSelection.js +14 -6
  150. package/Tag/TagGroup.d.ts +3 -0
  151. package/Tag/index.d.ts +2 -0
  152. package/Tag/index.js +1 -0
  153. package/Upload/UploadPictureCard.js +1 -1
  154. package/index.d.ts +36 -20
  155. package/index.js +26 -7
  156. package/package.json +4 -4
  157. package/utils/format-number-with-commas.d.ts +4 -0
  158. package/utils/format-number-with-commas.js +27 -0
  159. package/utils/parse-number-with-commas.d.ts +4 -0
  160. package/utils/parse-number-with-commas.js +22 -0
  161. package/Modal/ModalActions.d.ts +0 -9
  162. package/Modal/ModalActions.js +0 -20
  163. package/Modal/ModalBody.d.ts +0 -7
  164. package/Modal/ModalBody.js +0 -14
  165. package/Notification/Notification.d.ts +0 -54
  166. package/Notification/Notification.js +0 -76
  167. package/Notification/index.d.ts +0 -3
  168. package/Notification/index.js +0 -1
  169. package/Select/AutoComplete.d.ts +0 -107
  170. package/Select/AutoComplete.js +0 -114
  171. package/Table/hooks/useTableColumns.d.ts +0 -8
  172. package/Table/hooks/useTableColumns.js +0 -91
@@ -1,19 +1,38 @@
1
1
  import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
2
2
  export interface ModalHeaderProps extends NativeElementPropsWithoutKeyAndRef<'div'> {
3
3
  /**
4
- * Whether to show severity icon.
4
+ * Whether to show status type icon.
5
5
  * @default false
6
6
  */
7
- showSeverityIcon?: boolean;
7
+ showStatusTypeIcon?: boolean;
8
8
  /**
9
- * Controlls the title styles.
10
- * Use large title if the modal body has section/block titles.
11
- * @default false
9
+ * Layout of the status type icon relative to title.
10
+ * - 'vertical': Icon above title
11
+ * - 'horizontal': Icon to the left of title
12
+ * @default 'vertical'
13
+ */
14
+ statusTypeIconLayout?: 'vertical' | 'horizontal';
15
+ /**
16
+ * Supporting text displayed below the title.
17
+ */
18
+ supportingText?: string;
19
+ /**
20
+ * Alignment of the supporting text.
21
+ * @default 'left'
22
+ */
23
+ supportingTextAlign?: 'left' | 'center';
24
+ /**
25
+ * The title text of the modal header.
26
+ */
27
+ title: string;
28
+ /**
29
+ * Alignment of the title.
30
+ * @default 'left'
12
31
  */
13
- titleLarge?: boolean;
32
+ titleAlign?: 'left' | 'center';
14
33
  }
15
34
  /**
16
- * The react component for `mezzanine` modal header.
35
+ * The React component for `mezzanine` modal header.
17
36
  */
18
37
  declare const ModalHeader: import("react").ForwardRefExoticComponent<ModalHeaderProps & import("react").RefAttributes<HTMLDivElement>>;
19
38
  export default ModalHeader;
@@ -1,20 +1,46 @@
1
1
  'use client';
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
3
  import { forwardRef, useContext } from 'react';
4
- import { modalSeverityIcons, modalClasses } from '@mezzanine-ui/core/modal';
4
+ import { modalStatusTypeIcons, modalClasses } from '@mezzanine-ui/core/modal';
5
5
  import { ModalControlContext } from './ModalControl.js';
6
+ import Typography from '../Typography/Typography.js';
6
7
  import Icon from '../Icon/Icon.js';
7
8
  import cx from 'clsx';
8
9
 
9
10
  /**
10
- * The react component for `mezzanine` modal header.
11
+ * The React component for `mezzanine` modal header.
11
12
  */
12
13
  const ModalHeader = forwardRef(function ModalHeader(props, ref) {
13
- const { children, className, showSeverityIcon = false, titleLarge = false, ...rest } = props;
14
- const { severity } = useContext(ModalControlContext);
15
- return (jsxs("div", { ...rest, ref: ref, className: cx(modalClasses.header, className), children: [showSeverityIcon && (jsx(Icon, { className: modalClasses.severityIcon, icon: modalSeverityIcons[severity] })), jsx("h3", { className: cx(modalClasses.title, {
16
- [modalClasses.titleLarge]: titleLarge,
17
- }), title: typeof children === 'string' ? children : undefined, children: children })] }));
14
+ const { className, showStatusTypeIcon = false, statusTypeIconLayout = 'vertical', supportingText, supportingTextAlign = 'left', title, titleAlign = 'left', ...rest } = props;
15
+ const { modalStatusType } = useContext(ModalControlContext);
16
+ const iconColor = (type) => {
17
+ switch (type) {
18
+ case 'success':
19
+ return 'success-strong';
20
+ case 'warning':
21
+ return 'warning';
22
+ case 'error':
23
+ return 'error-solid';
24
+ case 'info':
25
+ return 'info-strong';
26
+ case 'email':
27
+ return 'info-strong';
28
+ case 'delete':
29
+ return 'error-solid';
30
+ default:
31
+ return 'neutral';
32
+ }
33
+ };
34
+ return (jsxs("div", { ...rest, ref: ref, className: cx(modalClasses.modalHeader, {
35
+ [modalClasses.modalHeader + '--horizontal']: statusTypeIconLayout === 'horizontal',
36
+ [modalClasses.modalHeader + '--vertical']: statusTypeIconLayout === 'vertical',
37
+ [modalClasses.modalHeader + '--title-align-left']: titleAlign === 'left',
38
+ [modalClasses.modalHeader + '--title-align-center']: titleAlign === 'center',
39
+ [modalClasses.modalHeader + '--show-modal-status-type-icon']: showStatusTypeIcon,
40
+ }, className), children: [showStatusTypeIcon && (jsx("div", { className: cx(modalClasses.modalHeaderStatusTypeIcon), children: jsx(Icon, { icon: modalStatusTypeIcons[modalStatusType], color: iconColor(modalStatusType), size: 20 }) })), jsxs("div", { className: cx(modalClasses.modalHeaderTitleAndSupportingTextContainer), children: [jsx(Typography, { variant: "h3", color: "text-neutral-solid", className: cx(modalClasses.modalHeaderTitle), title: typeof title === 'string' ? title : undefined, children: title }), jsx(Typography, { variant: "body", color: "text-neutral-strong", className: cx(modalClasses.modalHeaderSupportingText, {
41
+ [modalClasses.modalHeaderSupportingText + '--align-left']: supportingTextAlign === 'left',
42
+ [modalClasses.modalHeaderSupportingText + '--align-center']: supportingTextAlign === 'center',
43
+ }), children: supportingText })] })] }));
18
44
  });
19
45
 
20
46
  export { ModalHeader as default };
package/Modal/index.d.ts CHANGED
@@ -1,12 +1,11 @@
1
- export type { ModalSeverity, ModalSize } from '@mezzanine-ui/core/modal';
2
- export type { ModalActionsProps } from './ModalActions';
3
- export { default as ModalActions } from './ModalActions';
4
- export type { ModalBodyProps } from './ModalBody';
5
- export { default as ModalBody } from './ModalBody';
1
+ export type { ModalStatusType, ModalSize } from '@mezzanine-ui/core/modal';
2
+ export type { ModalBodyForVerificationProps } from './ModalBodyForVerification';
3
+ export { default as ModalBodyForVerification } from './ModalBodyForVerification';
6
4
  export type { ModalFooterProps } from './ModalFooter';
7
5
  export { default as ModalFooter } from './ModalFooter';
8
6
  export type { ModalHeaderProps } from './ModalHeader';
9
7
  export { default as ModalHeader } from './ModalHeader';
10
8
  export { default as useModalContainer } from './useModalContainer';
9
+ export type { ModalContainerProps } from './useModalContainer';
11
10
  export type { ModalProps } from './Modal';
12
11
  export { default } from './Modal';
package/Modal/index.js CHANGED
@@ -1,5 +1,4 @@
1
- export { default as ModalActions } from './ModalActions.js';
2
- export { default as ModalBody } from './ModalBody.js';
1
+ export { default as ModalBodyForVerification } from './ModalBodyForVerification.js';
3
2
  export { default as ModalFooter } from './ModalFooter.js';
4
3
  export { default as ModalHeader } from './ModalHeader.js';
5
4
  export { default as useModalContainer } from './useModalContainer.js';
@@ -1,5 +1,14 @@
1
- import { SlideFadeOverlayProps } from '../_internal/SlideFadeOverlay';
1
+ import { ReactNode } from 'react';
2
+ import { BackdropProps } from '../Backdrop';
3
+ export interface ModalContainerProps extends Pick<BackdropProps, 'className' | 'container' | 'disableCloseOnBackdropClick' | 'disablePortal' | 'onBackdropClick' | 'onClose' | 'open'> {
4
+ children?: ReactNode;
5
+ /**
6
+ * Controls whether to disable closing modal while escape key down.
7
+ * @default false
8
+ */
9
+ disableCloseOnEscapeKeyDown?: boolean;
10
+ }
2
11
  export default function useModalContainer(): {
3
- Container: import("react").ForwardRefExoticComponent<SlideFadeOverlayProps & import("react").RefAttributes<HTMLDivElement>>;
4
- defaultOptions: Pick<SlideFadeOverlayProps, "className" | "open" | "direction" | "disablePortal" | "disableCloseOnBackdropClick" | "disableCloseOnEscapeKeyDown">;
12
+ Container: import("react").ForwardRefExoticComponent<ModalContainerProps & import("react").RefAttributes<HTMLDivElement>>;
13
+ defaultOptions: Pick<ModalContainerProps, "className" | "open" | "disablePortal" | "disableCloseOnBackdropClick" | "disableCloseOnEscapeKeyDown">;
5
14
  };
@@ -1,19 +1,41 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
2
  import { modalClasses } from '@mezzanine-ui/core/modal';
3
- import { forwardRef } from 'react';
4
- import SlideFadeOverlay from '../_internal/SlideFadeOverlay/SlideFadeOverlay.js';
3
+ import { forwardRef, useState } from 'react';
4
+ import { useDocumentEscapeKeyDown } from '../hooks/useDocumentEscapeKeyDown.js';
5
+ import useTopStack from '../_internal/SlideFadeOverlay/useTopStack.js';
6
+ import Backdrop from '../Backdrop/Backdrop.js';
7
+ import Fade from '../Transition/Fade.js';
8
+ import cx from 'clsx';
5
9
 
6
10
  const defaultOptions = {
7
11
  className: modalClasses.overlay,
8
- direction: 'down',
9
12
  disableCloseOnBackdropClick: false,
10
13
  disableCloseOnEscapeKeyDown: false,
11
14
  disablePortal: false,
12
15
  open: false,
13
16
  };
14
- const ModalContainer = forwardRef((props, ref) => {
15
- const { className = defaultOptions.className, children, container, direction = defaultOptions.direction, disableCloseOnBackdropClick = defaultOptions.disableCloseOnBackdropClick, disableCloseOnEscapeKeyDown = defaultOptions.disableCloseOnEscapeKeyDown, disablePortal = defaultOptions.disablePortal, onBackdropClick, onClose, open = defaultOptions.open, } = props;
16
- return (jsx(SlideFadeOverlay, { className: className, container: container, direction: direction, disableCloseOnBackdropClick: disableCloseOnBackdropClick, disableCloseOnEscapeKeyDown: disableCloseOnEscapeKeyDown, disablePortal: disablePortal, onBackdropClick: onBackdropClick, onClose: onClose, open: open, ref: ref, children: children }));
17
+ const ModalContainer = forwardRef(function ModalContainer(props, ref) {
18
+ const { children, className = defaultOptions.className, container, disableCloseOnBackdropClick = defaultOptions.disableCloseOnBackdropClick, disableCloseOnEscapeKeyDown = defaultOptions.disableCloseOnEscapeKeyDown, disablePortal = defaultOptions.disablePortal, onBackdropClick, onClose, open = defaultOptions.open, } = props;
19
+ const [exited, setExited] = useState(true);
20
+ /**
21
+ * Escape keydown close: escape will only close the top modal
22
+ */
23
+ const checkIsOnTheTop = useTopStack(open);
24
+ useDocumentEscapeKeyDown(() => {
25
+ if (!open || disableCloseOnEscapeKeyDown || !onClose) {
26
+ return;
27
+ }
28
+ return (event) => {
29
+ if (checkIsOnTheTop()) {
30
+ event.stopPropagation();
31
+ onClose();
32
+ }
33
+ };
34
+ }, [disableCloseOnEscapeKeyDown, checkIsOnTheTop, open, onClose]);
35
+ if (!open && exited) {
36
+ return null;
37
+ }
38
+ return (jsx(Backdrop, { className: cx(className), container: container, disableCloseOnBackdropClick: disableCloseOnBackdropClick, disablePortal: disablePortal, onBackdropClick: onBackdropClick, onClose: onClose, open: open, role: "presentation", children: jsx(Fade, { in: open, onEntered: () => setExited(false), onExited: () => setExited(true), children: jsx("div", { ref: ref, children: children }) }) }));
17
39
  });
18
40
  function useModalContainer() {
19
41
  return {
@@ -0,0 +1,6 @@
1
+ import { FC } from 'react';
2
+ import { NavigationChild } from './Navigation';
3
+ export interface CollapsedMenuProps {
4
+ items: NavigationChild[];
5
+ }
6
+ export declare const CollapsedMenu: FC<CollapsedMenuProps>;
@@ -0,0 +1,16 @@
1
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
+ import { useState, useRef } from 'react';
3
+ import { navigationClasses } from '@mezzanine-ui/core/navigation';
4
+ import NavigationIconButton from './NavigationIconButton.js';
5
+ import { DotHorizontalIcon } from '@mezzanine-ui/icons';
6
+ import Popper from '../Popper/Popper.js';
7
+
8
+ const CollapsedMenu = ({ items }) => {
9
+ const [menuOpen, setMenuOpen] = useState(false);
10
+ const targetRef = useRef(null);
11
+ return (jsxs(Fragment, { children: [jsx(NavigationIconButton, { ref: targetRef, icon: DotHorizontalIcon, onClick: () => setMenuOpen(!menuOpen) }), jsx(Popper, { anchor: targetRef.current, open: menuOpen, options: {
12
+ placement: 'right-end',
13
+ }, children: jsx("div", { className: navigationClasses.collapsedMenu, children: jsx("ul", { children: items }) }) })] }));
14
+ };
15
+
16
+ export { CollapsedMenu };
@@ -1,10 +1,11 @@
1
- import { JSX, ReactElement } from 'react';
1
+ import { ReactElement } from 'react';
2
2
  import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
3
3
  import { NavigationOptionProps } from './NavigationOption';
4
4
  import { NavigationHeaderProps } from './NavigationHeader';
5
5
  import { NavigationFooterProps } from './NavigationFooter';
6
- export type NavigationChild = ReactElement<NavigationOptionProps | NavigationHeaderProps | NavigationFooterProps> | null | JSX.Element[];
7
- export type NavigationChildren = NavigationChild[];
6
+ import { NavigationOptionCategoryProps } from './NavigationOptionCategory';
7
+ export type NavigationChild = ReactElement<NavigationFooterProps> | ReactElement<NavigationHeaderProps> | ReactElement<NavigationOptionCategoryProps> | ReactElement<NavigationOptionProps> | null | undefined | false;
8
+ export type NavigationChildren = NavigationChild | NavigationChild[];
8
9
  export interface NavigationProps extends Omit<NativeElementPropsWithoutKeyAndRef<'ul'>, 'onClick'> {
9
10
  /**
10
11
  * Current active key.
@@ -14,6 +15,19 @@ export interface NavigationProps extends Omit<NativeElementPropsWithoutKeyAndRef
14
15
  * Strict children with `NavigationOption`, `NavigationHeader` or `NavigationFooter`.
15
16
  */
16
17
  children?: NavigationChildren;
18
+ /**
19
+ * Navigation display type.
20
+ * @default false (expanded)
21
+ */
22
+ collapsed?: boolean;
23
+ /**
24
+ * Whether to show search input
25
+ */
26
+ filter?: boolean;
27
+ /**
28
+ * Called when collapsed state changes.
29
+ */
30
+ onCollapseChange?: (collapsed: boolean) => void;
17
31
  /**
18
32
  * Called when a navigation option is clicked.
19
33
  */
@@ -1,5 +1,6 @@
1
- import { jsxs, jsx } from 'react/jsx-runtime';
2
- import { forwardRef, useState, useCallback, useMemo, Children, isValidElement, cloneElement } from 'react';
1
+ 'use client';
2
+ import { jsx, jsxs } from 'react/jsx-runtime';
3
+ import { forwardRef, useState, useCallback, useMemo, Children, isValidElement } from 'react';
3
4
  import { navigationClasses } from '@mezzanine-ui/core/navigation';
4
5
  import NavigationOption from './NavigationOption.js';
5
6
  import NavigationHeader from './NavigationHeader.js';
@@ -8,11 +9,19 @@ import { flattenChildren } from '../utils/flatten-children.js';
8
9
  import NavigationOptionCategory from './NavigationOptionCategory.js';
9
10
  import { NavigationActivatedContext, NavigationOptionLevelContext, navigationOptionLevelContextDefaultValues } from './context.js';
10
11
  import { useCurrentPathname } from './useCurrentPathname.js';
12
+ import { useVisibleItems } from './useVisibleItems.js';
13
+ import { CollapsedMenu } from './CollapsedMenu.js';
11
14
  import Input from '../Input/Input.js';
12
15
  import cx from 'clsx';
13
16
 
14
17
  const Navigation = forwardRef((props, ref) => {
15
- const { activatedPath, children = [], className, onOptionClick, ...rest } = props;
18
+ const { activatedPath, children = [], className, collapsed: collapsedProp, filter, onCollapseChange, onOptionClick, ...rest } = props;
19
+ const [collapsedState, setCollapsedState] = useState(collapsedProp || false);
20
+ const collapsed = collapsedProp !== null && collapsedProp !== void 0 ? collapsedProp : collapsedState;
21
+ const handleCollapseChange = useCallback((newCollapsed) => {
22
+ setCollapsedState(newCollapsed);
23
+ onCollapseChange === null || onCollapseChange === void 0 ? void 0 : onCollapseChange(newCollapsed);
24
+ }, [onCollapseChange]);
16
25
  const [innerActivatedPath, setInnerActivatedPath] = useState([]);
17
26
  const combineSetActivatedPath = useCallback((newActivatedPath) => {
18
27
  onOptionClick === null || onOptionClick === void 0 ? void 0 : onOptionClick(newActivatedPath);
@@ -20,11 +29,13 @@ const Navigation = forwardRef((props, ref) => {
20
29
  }, [onOptionClick]);
21
30
  const currentPathname = useCurrentPathname();
22
31
  const flattenedChildren = useMemo(() => flattenChildren(children), [children]);
23
- const { headerComponent, footerComponent, searchInput } = useMemo(() => {
32
+ const [filterText, setFilterText] = useState('');
33
+ const { headerComponent, footerComponent, items, level1Items } = useMemo(() => {
24
34
  let headerComponent = null;
25
35
  let footerComponent = null;
26
- let searchInput = null;
27
- Children.forEach(flattenedChildren, (child) => {
36
+ const items = [];
37
+ const level1Items = [];
38
+ Children.forEach(flattenedChildren, (child, index) => {
28
39
  if (child && isValidElement(child)) {
29
40
  switch (child.type) {
30
41
  case NavigationHeader: {
@@ -35,40 +46,44 @@ const Navigation = forwardRef((props, ref) => {
35
46
  footerComponent = child;
36
47
  break;
37
48
  }
38
- case Input: {
39
- searchInput = cloneElement(child, {
40
- size: 'sub',
41
- variant: 'search',
42
- });
43
- break;
44
- }
45
- }
46
- }
47
- });
48
- return { headerComponent, footerComponent, searchInput };
49
- }, [flattenedChildren]);
50
- const renderItemChildren = useCallback(function renderItemChildrenImpl(parsedChildren) {
51
- var _a;
52
- const childArray = Children.map(parsedChildren, (child) => {
53
- if (child && isValidElement(child)) {
54
- switch (child.type) {
55
49
  case NavigationOptionCategory:
50
+ level1Items.push(...(child.props.children
51
+ ? flattenChildren(child.props.children, -1, [
52
+ child.props.title || 'NavigationOptionCategory:' + index,
53
+ ])
54
+ : []));
55
+ items.push(child);
56
+ break;
56
57
  case NavigationOption: {
57
- return child;
58
+ level1Items.push(child);
59
+ items.push(child);
60
+ break;
58
61
  }
59
62
  default:
60
- return null;
63
+ console.warn('[Mezzanine][Navigation]: Navigation only accepts NavigationOption, NavigationOptionCategory, NavigationHeader or NavigationFooter as children.');
61
64
  }
62
65
  }
63
- return null;
64
66
  });
65
- return (_a = childArray === null || childArray === void 0 ? void 0 : childArray.filter((child) => child !== null)) !== null && _a !== void 0 ? _a : null;
66
- }, []);
67
- return (jsxs("nav", { ...rest, ref: ref, className: cx(navigationClasses.host, navigationClasses.vertical, className), children: [headerComponent, jsx(NavigationActivatedContext.Provider, { value: {
68
- activatedPath: activatedPath || innerActivatedPath,
69
- setActivatedPath: combineSetActivatedPath,
70
- currentPathname,
71
- }, children: jsx(NavigationOptionLevelContext.Provider, { value: navigationOptionLevelContextDefaultValues, children: jsxs("div", { className: navigationClasses.content, children: [searchInput, jsx("ul", { children: renderItemChildren(flattenedChildren) })] }) }) }), footerComponent] }));
67
+ return { headerComponent, footerComponent, items, level1Items };
68
+ }, [flattenedChildren]);
69
+ const { contentRef, visibleCount } = useVisibleItems(items, collapsed);
70
+ const { collapsedItems, collapsedMenuItems } = useMemo(() => {
71
+ return {
72
+ collapsedItems: visibleCount !== null
73
+ ? level1Items.slice(0, visibleCount)
74
+ : level1Items,
75
+ collapsedMenuItems: visibleCount !== null ? level1Items.slice(visibleCount) : [],
76
+ };
77
+ }, [level1Items, visibleCount]);
78
+ return (jsx("nav", { ...rest, ref: ref, className: cx(navigationClasses.host, collapsed ? navigationClasses.collapsed : navigationClasses.expand, className), children: jsxs(NavigationActivatedContext.Provider, { value: {
79
+ activatedPath: activatedPath || innerActivatedPath,
80
+ setActivatedPath: combineSetActivatedPath,
81
+ currentPathname,
82
+ collapsed,
83
+ handleCollapseChange,
84
+ }, children: [headerComponent, jsx(NavigationOptionLevelContext.Provider, { value: navigationOptionLevelContextDefaultValues, children: jsxs("div", { ref: contentRef, className: navigationClasses.content, children: [filter && (jsx(Input, { size: "sub", variant: "search", className: cx(navigationClasses.searchInput), value: filterText, onChange: (e) => setFilterText(e.target.value) })), jsxs("ul", { children: [collapsed ? collapsedItems : items, collapsed &&
85
+ visibleCount !== null &&
86
+ visibleCount < level1Items.length && (jsx(CollapsedMenu, { items: collapsedMenuItems }))] }, collapsed ? 'collapsed' : 'expand')] }) }), footerComponent] }) }));
72
87
  });
73
88
 
74
89
  export { Navigation as default };
@@ -1,7 +1,8 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
- import { forwardRef, Children, isValidElement } from 'react';
2
+ import { forwardRef, use, Children, isValidElement } from 'react';
3
3
  import { navigationFooterClasses } from '@mezzanine-ui/core/navigation';
4
4
  import NavigationUserMenu from './NavigationUserMenu.js';
5
+ import { NavigationActivatedContext } from './context.js';
5
6
  import cx from 'clsx';
6
7
 
7
8
  const resolveChildren = (children) => {
@@ -19,8 +20,9 @@ const resolveChildren = (children) => {
19
20
  };
20
21
  const NavigationFooter = forwardRef((props, ref) => {
21
22
  const { children, className, ...rest } = props;
23
+ const { collapsed } = use(NavigationActivatedContext);
22
24
  const { userMenu, otherChildren } = resolveChildren(children);
23
- return (jsxs("footer", { ...rest, ref: ref, className: cx(navigationFooterClasses.host, className), children: [userMenu, jsx("span", { className: navigationFooterClasses.icons, children: otherChildren })] }));
25
+ return (jsxs("footer", { ...rest, ref: ref, className: cx(navigationFooterClasses.host, collapsed && navigationFooterClasses.collapsed, className), children: [userMenu, jsx("span", { className: navigationFooterClasses.icons, children: otherChildren })] }));
24
26
  });
25
27
 
26
28
  export { NavigationFooter as default };
@@ -1,8 +1,18 @@
1
1
  import { ReactNode } from 'react';
2
2
  import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
3
3
  export interface NavigationHeaderProps extends NativeElementPropsWithoutKeyAndRef<'header'> {
4
- onCollapse?: () => void;
4
+ /**
5
+ * Custom content to render inside the header, typically an icon or logo.
6
+ */
5
7
  children?: ReactNode;
8
+ /**
9
+ * The title text displayed in the header.
10
+ */
11
+ title: string;
12
+ /**
13
+ * Callback function invoked when the brand area (logo and title) is clicked.
14
+ */
15
+ onBrandClick?: () => void;
6
16
  }
7
17
  declare const NavigationHeader: import("react").ForwardRefExoticComponent<NavigationHeaderProps & import("react").RefAttributes<HTMLElement>>;
8
18
  export default NavigationHeader;
@@ -1,13 +1,16 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
- import { forwardRef } from 'react';
2
+ import { forwardRef, use } from 'react';
3
3
  import { navigationHeaderClasses } from '@mezzanine-ui/core/navigation';
4
4
  import { SiderIcon } from '@mezzanine-ui/icons';
5
5
  import NavigationIconButton from './NavigationIconButton.js';
6
+ import { NavigationActivatedContext } from './context.js';
6
7
  import cx from 'clsx';
7
8
 
8
9
  const NavigationHeader = forwardRef((props, ref) => {
9
- const { children, className, onCollapse, ...rest } = props;
10
- return (jsxs("header", { ...rest, ref: ref, className: cx(navigationHeaderClasses.host, className), children: [jsx("span", { className: navigationHeaderClasses.content, children: children }), jsx(NavigationIconButton, { onClick: onCollapse, icon: SiderIcon })] }));
10
+ const { children, className, title, onBrandClick, ...rest } = props;
11
+ const { collapsed, handleCollapseChange } = use(NavigationActivatedContext);
12
+ const BrandComponent = onBrandClick ? 'button' : 'span';
13
+ return (jsxs("header", { ...rest, ref: ref, className: cx(navigationHeaderClasses.host, collapsed && navigationHeaderClasses.collapsed, className), children: [jsx(NavigationIconButton, { onClick: () => handleCollapseChange(!collapsed), icon: SiderIcon }), jsxs(BrandComponent, { type: "button", className: navigationHeaderClasses.content, onClick: onBrandClick, children: [children, jsx("span", { className: navigationHeaderClasses.title, children: title })] })] }));
11
14
  });
12
15
 
13
16
  export { NavigationHeader as default };
@@ -1,9 +1,10 @@
1
1
  import { ReactElement } from 'react';
2
2
  import { IconDefinition } from '@mezzanine-ui/icons';
3
3
  import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
4
- export type NavigationOptionChild = ReactElement<NavigationOptionProps>;
4
+ import { BadgeProps } from '../Badge';
5
+ export type NavigationOptionChild = ReactElement<NavigationOptionProps> | ReactElement<BadgeProps> | false | null | undefined;
5
6
  export type NavigationOptionChildren = NavigationOptionChild | NavigationOptionChild[];
6
- export interface NavigationOptionProps extends Omit<NativeElementPropsWithoutKeyAndRef<'li'>, 'onClick'> {
7
+ export interface NavigationOptionProps extends Omit<NativeElementPropsWithoutKeyAndRef<'li'>, 'onClick' | 'onMouseEnter' | 'onMouseLeave'> {
7
8
  /**
8
9
  * Whether the item is active.
9
10
  */
@@ -1,11 +1,12 @@
1
1
  'use client';
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
- import { forwardRef, useState, useRef, use, useMemo, useEffect } from 'react';
3
+ import { forwardRef, useState, use, useMemo, useEffect, Children, isValidElement } from 'react';
4
4
  import { navigationOptionClasses } from '@mezzanine-ui/core/navigation';
5
5
  import { ChevronUpIcon, ChevronDownIcon } from '@mezzanine-ui/icons';
6
- import { useClickAway } from '../hooks/useClickAway.js';
7
- import { useComposeRefs } from '../hooks/useComposeRefs.js';
8
6
  import { NavigationOptionLevelContext, NavigationActivatedContext } from './context.js';
7
+ import { flattenChildren } from '../utils/flatten-children.js';
8
+ import Badge from '../Badge/Badge.js';
9
+ import Tooltip from '../Tooltip/Tooltip.js';
9
10
  import Icon from '../Icon/Icon.js';
10
11
  import Collapse from '../Transition/Collapse.js';
11
12
  import cx from 'clsx';
@@ -13,48 +14,66 @@ import cx from 'clsx';
13
14
  const NavigationOption = forwardRef((props, ref) => {
14
15
  const { active, children, className, defaultOpen = false, href, icon, onTriggerClick, title, ...rest } = props;
15
16
  const [open, setOpen] = useState(defaultOpen);
16
- const nodeRef = useRef(null);
17
- const composedNodeRef = useComposeRefs([ref, nodeRef]);
18
17
  const GroupToggleIcon = open ? ChevronUpIcon : ChevronDownIcon;
19
- useClickAway(() => {
20
- if (!open) {
21
- return;
22
- }
23
- return () => {
24
- setOpen(!open);
25
- };
26
- }, nodeRef, [open]);
27
18
  const { level, path: parentPath } = use(NavigationOptionLevelContext);
28
19
  const currentLevel = level + 1;
29
20
  const currentKey = href || title || 'unknownId';
30
21
  const currentPath = useMemo(() => [...parentPath, currentKey], [parentPath, currentKey]);
31
- const { activatedPath, setActivatedPath, currentPathname } = use(NavigationActivatedContext);
22
+ const { activatedPath, setActivatedPath, currentPathname, collapsed, handleCollapseChange, } = use(NavigationActivatedContext);
32
23
  useEffect(() => {
33
24
  if (currentPathname === href) {
34
25
  setActivatedPath(currentPath);
26
+ setOpen(true);
35
27
  }
36
28
  // eslint-disable-next-line react-hooks/exhaustive-deps
37
29
  }, []);
38
30
  const Component = href ? 'a' : 'div';
39
- return (jsxs("li", { ...rest, ref: composedNodeRef, className: cx(navigationOptionClasses.host, open && navigationOptionClasses.open, !children && navigationOptionClasses.basic, (active !== null && active !== void 0 ? active : (activatedPath === null || activatedPath === void 0 ? void 0 : activatedPath[currentLevel - 1]) === currentKey) &&
40
- navigationOptionClasses.active, className), "data-id": currentKey, children: [jsxs(Component, { className: cx(navigationOptionClasses.content, navigationOptionClasses.level(currentLevel)), href: href, onClick: () => {
41
- setOpen(!open);
42
- onTriggerClick === null || onTriggerClick === void 0 ? void 0 : onTriggerClick(currentPath, href || '');
43
- if (!children)
44
- setActivatedPath([...parentPath, currentKey]);
45
- }, onKeyDown: (e) => {
46
- if (e.key === 'Enter' || e.key === ' ') {
47
- e.preventDefault();
31
+ const flattenedChildren = useMemo(() => flattenChildren(children), [children]);
32
+ const { badge, items } = useMemo(() => {
33
+ let badgeComponent = null;
34
+ const items = [];
35
+ Children.forEach(flattenedChildren, (child) => {
36
+ if (child && isValidElement(child)) {
37
+ switch (child.type) {
38
+ case Badge: {
39
+ badgeComponent = child;
40
+ break;
41
+ }
42
+ case NavigationOption: {
43
+ items.push(child);
44
+ break;
45
+ }
46
+ default:
47
+ console.warn('[Mezzanine][NavigationOption]: NavigationOption only accepts NavigationOption or Badge as children.');
48
+ }
49
+ }
50
+ });
51
+ return { badge: badgeComponent, items };
52
+ }, [flattenedChildren]);
53
+ return (jsxs("li", { ...rest, ref: ref, className: cx(navigationOptionClasses.host, open && navigationOptionClasses.open, !children && navigationOptionClasses.basic, (active !== null && active !== void 0 ? active : (activatedPath === null || activatedPath === void 0 ? void 0 : activatedPath[currentLevel - 1]) === currentKey) &&
54
+ navigationOptionClasses.active, collapsed && navigationOptionClasses.collapsed, className), "data-id": currentKey, children: [jsx(Tooltip, { options: {
55
+ placement: 'right',
56
+ }, title: collapsed ? title : undefined, children: ({ onMouseEnter, onMouseLeave, ref: tooltipChildRef }) => (jsxs(Component, { className: cx(navigationOptionClasses.content, navigationOptionClasses.level(currentLevel)), onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, ref: tooltipChildRef, href: href, onClick: () => {
48
57
  setOpen(!open);
58
+ onTriggerClick === null || onTriggerClick === void 0 ? void 0 : onTriggerClick(currentPath, href || '');
59
+ if (collapsed) {
60
+ handleCollapseChange(false);
61
+ }
49
62
  if (!children)
50
63
  setActivatedPath([...parentPath, currentKey]);
51
- }
52
- }, role: "menuitem", tabIndex: 0, children: [icon && jsx(Icon, { className: navigationOptionClasses.icon, icon: icon }), jsx("span", { className: navigationOptionClasses.title, children: title }), children && (jsx(Icon, { size: 12, className: navigationOptionClasses.toggleIcon, icon: GroupToggleIcon }))] }), children && (jsx(Collapse, { style: {
64
+ }, onKeyDown: (e) => {
65
+ if (e.key === 'Enter' || e.key === ' ') {
66
+ e.preventDefault();
67
+ setOpen(!open);
68
+ if (!children)
69
+ setActivatedPath([...parentPath, currentKey]);
70
+ }
71
+ }, role: "menuitem", tabIndex: 0, children: [icon && jsx(Icon, { className: navigationOptionClasses.icon, icon: icon }), jsx("span", { className: navigationOptionClasses.title, children: title }), badge, children && (jsx(Icon, { className: navigationOptionClasses.toggleIcon, icon: GroupToggleIcon }))] })) }), children && !collapsed && (jsx(Collapse, { className: navigationOptionClasses.childrenWrapper, style: {
53
72
  width: '100%',
54
73
  }, in: !!open, children: jsx(NavigationOptionLevelContext.Provider, { value: {
55
74
  level: currentLevel,
56
75
  path: currentPath,
57
- }, children: jsx("ul", { className: navigationOptionClasses.group, children: children }) }) }))] }));
76
+ }, children: jsx("ul", { className: navigationOptionClasses.group, children: items }) }) }))] }));
58
77
  });
59
78
 
60
79
  export { NavigationOption as default };
@@ -1,12 +1,30 @@
1
1
  'use client';
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
- import { forwardRef } from 'react';
3
+ import { forwardRef, useCallback, Children, isValidElement } from 'react';
4
4
  import { navigationOptionCategoryClasses } from '@mezzanine-ui/core/navigation';
5
+ import NavigationOption from './NavigationOption.js';
5
6
  import cx from 'clsx';
6
7
 
7
8
  const NavigationOptionCategory = forwardRef((props, ref) => {
8
9
  const { children, className, title, ...rest } = props;
9
- return (jsxs("li", { ...rest, ref: ref, className: cx(navigationOptionCategoryClasses.host, className), role: "menuitem", children: [jsx("span", { className: navigationOptionCategoryClasses.title, children: title }), jsx("ul", { children: children })] }));
10
+ const renderItemChildren = useCallback(function renderItemChildrenImpl(parsedChildren) {
11
+ var _a;
12
+ const childArray = Children.map(parsedChildren, (child) => {
13
+ if (child && isValidElement(child)) {
14
+ switch (child.type) {
15
+ case NavigationOption: {
16
+ return child;
17
+ }
18
+ default:
19
+ console.warn('[Mezzanine][NavigationOptionCategory]: NavigationOptionCategory only accepts NavigationOption as children.');
20
+ return null;
21
+ }
22
+ }
23
+ return null;
24
+ });
25
+ return (_a = childArray === null || childArray === void 0 ? void 0 : childArray.filter((child) => child !== null)) !== null && _a !== void 0 ? _a : null;
26
+ }, []);
27
+ return (jsxs("li", { ...rest, ref: ref, className: cx(navigationOptionCategoryClasses.host, className), role: "menuitem", children: [jsx("span", { className: navigationOptionCategoryClasses.title, children: title }), jsx("ul", { children: renderItemChildren(children) })] }));
10
28
  });
11
29
 
12
30
  export { NavigationOptionCategory as default };
@@ -10,4 +10,6 @@ export declare const NavigationActivatedContext: import("react").Context<{
10
10
  activatedPath: string[];
11
11
  setActivatedPath: (path: string[]) => void;
12
12
  currentPathname: string | null;
13
+ collapsed: boolean;
14
+ handleCollapseChange: (newCollapsed: boolean) => void;
13
15
  }>;
@@ -0,0 +1,5 @@
1
+ import { NavigationChild } from './Navigation';
2
+ export declare function useVisibleItems(items: NavigationChild[], collapsed: boolean): {
3
+ contentRef: import("react").RefObject<HTMLDivElement | null>;
4
+ visibleCount: number | null;
5
+ };