@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
@@ -33,11 +33,12 @@ function DropdownItemCard(props) {
33
33
  const isControlled = checked !== undefined;
34
34
  const [internalChecked, setInternalChecked] = useState(defaultChecked !== null && defaultChecked !== void 0 ? defaultChecked : false);
35
35
  const isChecked = isControlled ? checked : internalChecked;
36
- const labelColor = useMemo(() => {
37
- return validate === 'danger' ? 'text-error' : 'text-neutral-solid';
38
- }, [validate]);
39
36
  const appendIconColor = useMemo(() => {
40
- return disabled || validate === 'danger' ? 'neutral-light' : 'neutral';
37
+ if (disabled)
38
+ return 'neutral-light';
39
+ if (validate === 'danger')
40
+ return 'error';
41
+ return 'brand';
41
42
  }, [disabled, validate]);
42
43
  const iconColor = useMemo(() => {
43
44
  if (disabled)
@@ -55,10 +56,10 @@ function DropdownItemCard(props) {
55
56
  ];
56
57
  }, [cardLabel, followText]);
57
58
  const showPrependContent = useMemo(() => {
58
- return prependIcon || (checkSite === 'prepend' && mode === 'multiple');
59
+ return prependIcon || (checkSite === 'prefix' && mode === 'multiple');
59
60
  }, [prependIcon, checkSite, mode]);
60
61
  const showAppendContent = useMemo(() => {
61
- return appendContent || appendIcon || (checkSite === 'append' && isChecked);
62
+ return appendContent || appendIcon || (checkSite === 'suffix' && isChecked);
62
63
  }, [appendContent, appendIcon, checkSite, isChecked]);
63
64
  const subTitleParts = useMemo(() => {
64
65
  return followText && subTitle
@@ -72,8 +73,8 @@ function DropdownItemCard(props) {
72
73
  ]
73
74
  : [];
74
75
  }, [subTitle, followText]);
75
- const renderHighlightedText = (parts, defaultColor, className, id) => {
76
- return (jsx(Typography, { color: defaultColor, className: className, id: id, children: parts.map((part, index) => (jsx("span", { className: part.highlight && validate !== 'danger' ? dropdownClasses.cardHighlightedText : '', children: part.text }, index))) }));
76
+ const renderHighlightedText = (parts, className, id) => {
77
+ return (jsx(Typography, { className: className, id: id, children: parts.map((part, index) => (jsx("span", { className: part.highlight && validate !== 'danger' ? dropdownClasses.cardHighlightedText : '', children: part.text }, index))) }));
77
78
  };
78
79
  const toggleChecked = () => {
79
80
  if (disabled)
@@ -105,11 +106,11 @@ function DropdownItemCard(props) {
105
106
  }
106
107
  };
107
108
  return (jsxs(Fragment, { children: [jsx("li", { ...(labelId ? { 'aria-labelledby': labelId } : {}), ...(ariaLabel ? { 'aria-label': ariaLabel } : {}), "aria-selected": active, className: cx(dropdownClasses.card, dropdownClasses.cardLevel(level), {
108
- // Highlight: keyboard/mouse focused (active) or selected (isChecked)
109
109
  [dropdownClasses.cardActive]: active || isChecked,
110
110
  [dropdownClasses.cardDisabled]: disabled,
111
+ [dropdownClasses.cardDanger]: validate === 'danger',
111
112
  }, className), id: id, role: "option", tabIndex: -1, onMouseEnter: onMouseEnter, onClick: handleClick, onKeyDown: handleKeyDown, children: jsxs("div", { className: dropdownClasses.cardContainer, children: [showPrependContent && (jsxs("div", { className: dropdownClasses.cardPrependContent, children: [prependIcon
112
- && jsx(Icon, { icon: prependIcon, color: iconColor }), checkSite === 'prepend' && mode === 'multiple' && (jsx(Checkbox, { checked: isChecked, disabled: disabled, onChange: handleCheckboxChange }))] })), jsxs("div", { className: dropdownClasses.cardBody, children: [cardLabel && renderHighlightedText(labelParts, labelColor, dropdownClasses.cardTitle, labelId), subTitleParts.length > 0 && renderHighlightedText(subTitleParts, 'text-neutral', dropdownClasses.cardDescription)] }), showAppendContent && (jsxs("div", { className: dropdownClasses.cardAppendContent, children: [appendContent && jsx(Typography, { color: "text-neutral-light", children: appendContent }), appendIcon && jsx(Icon, { icon: appendIcon, color: iconColor }), checkSite === 'append' && isChecked && jsx(Icon, { icon: CheckedIcon, color: appendIconColor, size: 16 })] }))] }) }), showUnderline && jsx("div", { className: dropdownClasses.cardUnderline })] }));
113
+ && jsx(Icon, { icon: prependIcon, color: iconColor }), checkSite === 'prefix' && mode === 'multiple' && (jsx(Checkbox, { checked: isChecked, disabled: disabled, onChange: handleCheckboxChange }))] })), jsxs("div", { className: dropdownClasses.cardBody, children: [cardLabel && renderHighlightedText(labelParts, dropdownClasses.cardTitle, labelId), subTitleParts.length > 0 && renderHighlightedText(subTitleParts, dropdownClasses.cardDescription)] }), showAppendContent && (jsxs("div", { className: dropdownClasses.cardAppendContent, children: [appendContent && jsx(Typography, { color: "text-neutral-light", children: appendContent }), appendIcon && jsx(Icon, { icon: appendIcon, color: iconColor }), checkSite === 'suffix' && isChecked && jsx(Icon, { icon: CheckedIcon, color: appendIconColor, size: 16 })] }))] }) }), showUnderline && jsx("div", { className: dropdownClasses.cardUnderline })] }));
113
114
  }
114
115
 
115
116
  export { DropdownItemCard as default };
@@ -1,11 +1,11 @@
1
- import { dropdownStatus } from "@mezzanine-ui/core/dropdown/dropdown";
1
+ import { DropdownStatus as DropdownStatusType } from "@mezzanine-ui/core/dropdown/dropdown";
2
2
  import { IconDefinition } from "@mezzanine-ui/icons";
3
3
  export interface DropdownStatusProps {
4
4
  /**
5
5
  * The status of the dropdown.
6
6
  * @default 'loading'
7
7
  */
8
- status: dropdownStatus;
8
+ status: DropdownStatusType;
9
9
  /**
10
10
  * The text of the dropdown loading status.
11
11
  */
@@ -0,0 +1,29 @@
1
+ 'use client';
2
+ import { jsx, jsxs } from 'react/jsx-runtime';
3
+ import { useMemo } from 'react';
4
+ import { dropdownClasses } from '@mezzanine-ui/core/dropdown/dropdown';
5
+ import { SpinnerIcon, FolderOpenIcon } from '@mezzanine-ui/icons';
6
+ import Typography from '../Typography/Typography.js';
7
+ import Icon from '../Icon/Icon.js';
8
+
9
+ function DropdownStatus(props) {
10
+ const { status, loadingText, emptyText, emptyIcon } = props;
11
+ const defaultStatusText = useMemo(() => {
12
+ if (status === 'loading') {
13
+ return loadingText !== null && loadingText !== void 0 ? loadingText : 'Loading...';
14
+ }
15
+ if (status === 'empty') {
16
+ return emptyText !== null && emptyText !== void 0 ? emptyText : 'No matching options.';
17
+ }
18
+ return '';
19
+ }, [status, loadingText, emptyText]);
20
+ const IconElement = useMemo(() => {
21
+ if (status === 'loading') {
22
+ return jsx(Icon, { icon: SpinnerIcon, size: 16, spin: true, color: "brand" });
23
+ }
24
+ return jsx(Icon, { icon: emptyIcon !== null && emptyIcon !== void 0 ? emptyIcon : FolderOpenIcon, size: 16 });
25
+ }, [status, emptyIcon]);
26
+ return (jsxs("div", { className: dropdownClasses.status, children: [IconElement, jsx(Typography, { className: dropdownClasses.statusText, children: defaultStatusText })] }));
27
+ }
28
+
29
+ export { DropdownStatus as default };
@@ -1,4 +1,5 @@
1
1
  import { DropdownOption } from '@mezzanine-ui/core/dropdown/dropdown';
2
+ import { Dispatch, SetStateAction } from 'react';
2
3
  /**
3
4
  * Provides a keyboard navigation handler for dropdown lists, encapsulating Arrow keys, Enter, and Escape behaviors.
4
5
  * Keeps logic centralized in DropdownItem for easy reuse.
@@ -9,7 +10,7 @@ export declare function createDropdownKeydownHandler(params: {
9
10
  onEscape?: () => void;
10
11
  open: boolean;
11
12
  options: DropdownOption[];
12
- setActiveIndex: (updater: (prev: number | null) => number | null) => void;
13
+ setActiveIndex: Dispatch<SetStateAction<number | null>>;
13
14
  setListboxHasVisualFocus: (focus: boolean) => void;
14
15
  setOpen: (open: boolean) => void;
15
16
  }): (e: React.KeyboardEvent<HTMLInputElement>) => void;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Provides a keyboard navigation handler for dropdown lists, encapsulating Arrow keys, Enter, and Escape behaviors.
3
+ * Keeps logic centralized in DropdownItem for easy reuse.
4
+ */
5
+ function createDropdownKeydownHandler(params) {
6
+ const { activeIndex, onEnterSelect, onEscape, open, options, setActiveIndex, setListboxHasVisualFocus, setOpen, } = params;
7
+ return (e) => {
8
+ if (options.length === 0)
9
+ return;
10
+ switch (e.key) {
11
+ case 'ArrowDown': {
12
+ e.preventDefault();
13
+ e.stopPropagation();
14
+ if (!open) {
15
+ setOpen(true);
16
+ setListboxHasVisualFocus(true);
17
+ setActiveIndex(() => 0);
18
+ return;
19
+ }
20
+ setListboxHasVisualFocus(true);
21
+ setActiveIndex((prev) => {
22
+ if (prev === null)
23
+ return 0;
24
+ return prev >= options.length - 1 ? 0 : prev + 1;
25
+ });
26
+ break;
27
+ }
28
+ case 'ArrowUp': {
29
+ e.preventDefault();
30
+ e.stopPropagation();
31
+ if (!open) {
32
+ setOpen(true);
33
+ setListboxHasVisualFocus(true);
34
+ setActiveIndex(() => options.length - 1);
35
+ return;
36
+ }
37
+ setListboxHasVisualFocus(true);
38
+ setActiveIndex((prev) => {
39
+ if (prev === null)
40
+ return options.length - 1;
41
+ return prev <= 0 ? options.length - 1 : prev - 1;
42
+ });
43
+ break;
44
+ }
45
+ case 'Enter': {
46
+ if (!open)
47
+ return;
48
+ e.preventDefault();
49
+ e.stopPropagation();
50
+ if (activeIndex !== null && options[activeIndex]) {
51
+ onEnterSelect === null || onEnterSelect === void 0 ? void 0 : onEnterSelect(options[activeIndex]);
52
+ }
53
+ break;
54
+ }
55
+ case 'Escape': {
56
+ e.preventDefault();
57
+ e.stopPropagation();
58
+ onEscape === null || onEscape === void 0 ? void 0 : onEscape();
59
+ break;
60
+ }
61
+ case 'Home':
62
+ case 'End':
63
+ case 'ArrowLeft':
64
+ case 'ArrowRight': {
65
+ setListboxHasVisualFocus(false);
66
+ setActiveIndex(() => null);
67
+ break;
68
+ }
69
+ }
70
+ };
71
+ }
72
+
73
+ export { createDropdownKeydownHandler };
@@ -8,7 +8,11 @@ function highlightText(text, keyword) {
8
8
  return [];
9
9
  if (!keyword)
10
10
  return [{ text, highlight: false }];
11
- const safeKeyword = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
11
+ // Ensure keyword is a string
12
+ const keywordString = String(keyword);
13
+ if (!keywordString)
14
+ return [{ text, highlight: false }];
15
+ const safeKeyword = keywordString.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
12
16
  const regex = new RegExp(`(${safeKeyword})`, 'gi');
13
17
  const parts = [];
14
18
  let lastIndex = 0;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Converts a shortcut key string into a formatted display string.
3
+ * @param shortcut - Shortcut key string (e.g., 'cmd+n', 'ctrl+n', 'k')
4
+ * @param isMac - Whether to format as Mac shortcut
5
+ * @returns Formatted display string
6
+ */
7
+ export declare const formatShortcut: (shortcut: string | number, isMac: boolean) => string;
8
+ /**
9
+ * Converts an array of shortcut keys into a display string.
10
+ * Format: {mac_shortcut} / {windows_shortcut} / {other_keys}
11
+ *
12
+ * @param shortcutKeys - Array of shortcut keys
13
+ * @returns Formatted display string
14
+ *
15
+ * @example
16
+ * shortcutTextHandler(['ctrl+n', 'cmd+n']) // '⌘N / Ctrl+N'
17
+ * shortcutTextHandler(['delete', 'backspace']) // 'Delete / Backspace'
18
+ * shortcutTextHandler(['k']) // 'K'
19
+ * shortcutTextHandler(['ctrl+r', 'cmd+r', 'f5']) // '⌘R / Ctrl+R / F5'
20
+ * shortcutTextHandler(['cmd+shift+n', 'ctrl+shift+n']) // '⌘⇧N / Ctrl+Shift+N'
21
+ * shortcutTextHandler(['cmd+option+n', 'ctrl+alt+n']) // '⌘⌥N / Ctrl+Alt+N'
22
+ */
23
+ export declare const shortcutTextHandler: (shortcutKeys: Array<string | number>) => string;
24
+ export default shortcutTextHandler;
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Parses a shortcut key string into modifiers and main key.
3
+ * @param shortcut - Shortcut key string (e.g., 'cmd+shift+n', 'ctrl+alt+delete')
4
+ * @returns Object with modifiers and main key
5
+ */
6
+ const parseShortcut = (shortcut) => {
7
+ const str = String(shortcut).toLowerCase();
8
+ const tokens = str
9
+ .split('+')
10
+ .map((token) => token.trim())
11
+ .filter(Boolean);
12
+ const modifiers = [];
13
+ let mainKey = null;
14
+ tokens.forEach((token) => {
15
+ switch (token) {
16
+ case 'cmd':
17
+ case 'meta':
18
+ case 'command':
19
+ modifiers.push('cmd');
20
+ break;
21
+ case 'ctrl':
22
+ case 'control':
23
+ modifiers.push('ctrl');
24
+ break;
25
+ case 'alt':
26
+ case 'option':
27
+ modifiers.push(token === 'option' ? 'option' : 'alt');
28
+ break;
29
+ case 'shift':
30
+ modifiers.push('shift');
31
+ break;
32
+ default:
33
+ if (!mainKey) {
34
+ mainKey = token;
35
+ }
36
+ break;
37
+ }
38
+ });
39
+ return { modifiers, mainKey: mainKey || str };
40
+ };
41
+ /**
42
+ * Formats a shortcut for Mac display.
43
+ * @param modifiers - Array of modifier keys
44
+ * @param mainKey - Main key
45
+ * @returns Formatted Mac shortcut string
46
+ */
47
+ const formatMacShortcut = (modifiers, mainKey) => {
48
+ const symbols = [];
49
+ modifiers.forEach((mod) => {
50
+ switch (mod) {
51
+ case 'cmd':
52
+ symbols.push('⌘');
53
+ break;
54
+ case 'option':
55
+ symbols.push('⌥');
56
+ break;
57
+ case 'shift':
58
+ symbols.push('⇧');
59
+ break;
60
+ case 'ctrl':
61
+ symbols.push('⌃');
62
+ break;
63
+ }
64
+ });
65
+ const formattedKey = mainKey.toUpperCase();
66
+ if (symbols.length > 0) {
67
+ return `${symbols.join('')}${formattedKey}`;
68
+ }
69
+ return formattedKey;
70
+ };
71
+ /**
72
+ * Formats a shortcut for Windows display.
73
+ * @param modifiers - Array of modifier keys
74
+ * @param mainKey - Main key
75
+ * @returns Formatted Windows shortcut string
76
+ */
77
+ const formatWindowsShortcut = (modifiers, mainKey) => {
78
+ const parts = [];
79
+ modifiers.forEach((mod) => {
80
+ switch (mod) {
81
+ case 'ctrl':
82
+ parts.push('Ctrl');
83
+ break;
84
+ case 'alt':
85
+ parts.push('Alt');
86
+ break;
87
+ case 'shift':
88
+ parts.push('Shift');
89
+ break;
90
+ case 'cmd':
91
+ // Windows doesn't have cmd, but if present, treat as Ctrl
92
+ parts.push('Ctrl');
93
+ break;
94
+ case 'option':
95
+ // Option on Mac is Alt on Windows
96
+ parts.push('Alt');
97
+ break;
98
+ }
99
+ });
100
+ const formattedKey = mainKey.charAt(0).toUpperCase() + mainKey.slice(1).toLowerCase();
101
+ if (parts.length > 0) {
102
+ return `${parts.join('+')}+${formattedKey}`;
103
+ }
104
+ return formattedKey;
105
+ };
106
+ /**
107
+ * Converts a shortcut key string into a formatted display string.
108
+ * @param shortcut - Shortcut key string (e.g., 'cmd+n', 'ctrl+n', 'k')
109
+ * @param isMac - Whether to format as Mac shortcut
110
+ * @returns Formatted display string
111
+ */
112
+ const formatShortcut = (shortcut, isMac) => {
113
+ const { modifiers, mainKey } = parseShortcut(shortcut);
114
+ if (isMac) {
115
+ return formatMacShortcut(modifiers, mainKey);
116
+ }
117
+ return formatWindowsShortcut(modifiers, mainKey);
118
+ };
119
+ /**
120
+ * Converts an array of shortcut keys into a display string.
121
+ * Format: {mac_shortcut} / {windows_shortcut} / {other_keys}
122
+ *
123
+ * @param shortcutKeys - Array of shortcut keys
124
+ * @returns Formatted display string
125
+ *
126
+ * @example
127
+ * shortcutTextHandler(['ctrl+n', 'cmd+n']) // '⌘N / Ctrl+N'
128
+ * shortcutTextHandler(['delete', 'backspace']) // 'Delete / Backspace'
129
+ * shortcutTextHandler(['k']) // 'K'
130
+ * shortcutTextHandler(['ctrl+r', 'cmd+r', 'f5']) // '⌘R / Ctrl+R / F5'
131
+ * shortcutTextHandler(['cmd+shift+n', 'ctrl+shift+n']) // '⌘⇧N / Ctrl+Shift+N'
132
+ * shortcutTextHandler(['cmd+option+n', 'ctrl+alt+n']) // '⌘⌥N / Ctrl+Alt+N'
133
+ */
134
+ const shortcutTextHandler = (shortcutKeys) => {
135
+ if (!shortcutKeys || shortcutKeys.length === 0) {
136
+ return '';
137
+ }
138
+ const mac = [];
139
+ const windows = [];
140
+ const others = [];
141
+ shortcutKeys.forEach((shortcut) => {
142
+ const { modifiers } = parseShortcut(shortcut);
143
+ const hasMacModifier = modifiers.includes('cmd') || modifiers.includes('option');
144
+ const hasWindowsModifier = modifiers.includes('ctrl') || modifiers.includes('alt');
145
+ if (hasMacModifier) {
146
+ mac.push(formatShortcut(shortcut, true));
147
+ }
148
+ else if (hasWindowsModifier) {
149
+ windows.push(formatShortcut(shortcut, false));
150
+ }
151
+ else {
152
+ // Single key or key with only shift
153
+ if (modifiers.includes('shift')) {
154
+ // Shift can be used on both platforms
155
+ mac.push(formatShortcut(shortcut, true));
156
+ windows.push(formatShortcut(shortcut, false));
157
+ }
158
+ else {
159
+ others.push(formatShortcut(shortcut, false));
160
+ }
161
+ }
162
+ });
163
+ // Remove duplicates and combine
164
+ const uniqueMac = Array.from(new Set(mac));
165
+ const uniqueWindows = Array.from(new Set(windows));
166
+ const uniqueOthers = Array.from(new Set(others));
167
+ const parts = [...uniqueMac, ...uniqueWindows, ...uniqueOthers];
168
+ return parts.join(' / ');
169
+ };
170
+
171
+ export { shortcutTextHandler as default, formatShortcut, shortcutTextHandler };
@@ -1,8 +1,8 @@
1
- import { Severity } from '@mezzanine-ui/system/severity';
1
+ import { SeverityWithInfo } from '@mezzanine-ui/system/severity';
2
2
  export interface FormControl {
3
3
  disabled: boolean;
4
4
  fullWidth: boolean;
5
5
  required: boolean;
6
- severity?: Severity;
6
+ severity?: SeverityWithInfo;
7
7
  }
8
8
  export declare const FormControlContext: import("react").Context<FormControl | undefined>;
@@ -1,6 +1,18 @@
1
- import { Severity } from '@mezzanine-ui/system/severity';
1
+ import { FormFieldCounterColor, FormFieldSize } from '@mezzanine-ui/core/form';
2
2
  import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
3
+ import { IconDefinition } from '@mezzanine-ui/icons';
4
+ import { SeverityWithInfo } from '@mezzanine-ui/system/severity';
3
5
  export interface FormFieldProps extends NativeElementPropsWithoutKeyAndRef<'div'> {
6
+ /**
7
+ * The counter text to display in the form field.
8
+ * Typically used to show character count or remaining characters.
9
+ */
10
+ counter?: string;
11
+ /**
12
+ * The color of the counter text.
13
+ * @default FormFieldCounterColor.INFO
14
+ */
15
+ counterColor?: FormFieldCounterColor;
4
16
  /**
5
17
  * To control the field passed from children whether should be disabled.
6
18
  * The form message won't appear if disabled.
@@ -10,17 +22,57 @@ export interface FormFieldProps extends NativeElementPropsWithoutKeyAndRef<'div'
10
22
  * To control the field passed from children whether should be fullWidth.
11
23
  */
12
24
  fullWidth?: boolean;
25
+ /**
26
+ * The hint text to display below the input field.
27
+ * Provides additional information or guidance to the user.
28
+ */
29
+ hintText?: string;
30
+ /**
31
+ * The icon to display alongside the hint text.
32
+ */
33
+ hintTextIcon?: IconDefinition;
34
+ /**
35
+ * The label text for the form field.
36
+ */
37
+ label: string;
38
+ /**
39
+ * The icon to display next to the label.
40
+ * When provided, displays an icon that shows a tooltip on hover.
41
+ */
42
+ labelInformationIcon?: IconDefinition;
43
+ /**
44
+ * The tooltip text to display when hovering over the label information icon.
45
+ * Only shown when labelInformationIcon is provided.
46
+ */
47
+ labelInformationText?: string;
48
+ /**
49
+ * Optional marker text to display after the label.
50
+ * Typically used to show "(optional)" or similar text.
51
+ */
52
+ labelOptionalMarker?: string;
53
+ /**
54
+ * The name attribute for the form field.
55
+ * Used to identify the field in form submissions and as htmlFor in the label.
56
+ */
57
+ name: string;
13
58
  /**
14
59
  * To control the field passed from children whether should be required.
15
60
  */
16
61
  required?: boolean;
17
62
  /**
18
- * To control the severity of field passed from children and form message.
63
+ * The size variant of the form field.
64
+ * Controls the layout and spacing of label, input, and other elements.
65
+ */
66
+ size: FormFieldSize;
67
+ /**
68
+ * The severity level of the form field.
69
+ * Used to indicate the importance or urgency of the field.
70
+ * @default 'info'
19
71
  */
20
- severity?: Severity;
72
+ severity?: SeverityWithInfo;
21
73
  }
22
74
  /**
23
- * The react component for `mezzanine` form field.
75
+ * The React component for `mezzanine` form field.
24
76
  */
25
77
  declare const FormField: import("react").ForwardRefExoticComponent<FormFieldProps & import("react").RefAttributes<HTMLDivElement>>;
26
78
  export default FormField;
package/Form/FormField.js CHANGED
@@ -1,24 +1,28 @@
1
- import { jsx } from 'react/jsx-runtime';
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { forwardRef } from 'react';
3
- import { formFieldClasses } from '@mezzanine-ui/core/form';
3
+ import { formFieldClasses, FormFieldCounterColor } from '@mezzanine-ui/core/form';
4
4
  import { FormControlContext } from './FormControlContext.js';
5
+ import FormHintText from './FormHintText.js';
6
+ import FormLabel from './FormLabel.js';
5
7
  import cx from 'clsx';
6
8
 
7
9
  /**
8
- * The react component for `mezzanine` form field.
10
+ * The React component for `mezzanine` form field.
9
11
  */
10
12
  const FormField = forwardRef(function FormField(props, ref) {
11
- const { children, className, disabled = false, fullWidth = false, required = false, severity, ...rest } = props;
13
+ const { children, className, counter, counterColor, disabled = false, fullWidth = false, hintText, hintTextIcon, label, labelInformationIcon, labelInformationText, labelOptionalMarker, name, required = false, size, severity = 'info', ...rest } = props;
12
14
  const formControl = {
13
15
  disabled,
14
16
  fullWidth,
15
17
  required,
16
18
  severity,
17
19
  };
18
- return (jsx("div", { ...rest, ref: ref, className: cx(formFieldClasses.host, severity && formFieldClasses.severity(severity), {
20
+ return (jsx("div", { ...rest, ref: ref, className: cx(formFieldClasses.host, formFieldClasses.size(size), {
19
21
  [formFieldClasses.disabled]: disabled,
20
22
  [formFieldClasses.fullWidth]: fullWidth,
21
- }, className), children: jsx(FormControlContext.Provider, { value: formControl, children: children }) }));
23
+ }, className), children: jsxs(FormControlContext.Provider, { value: formControl, children: [jsx(FormLabel, { className: formFieldClasses.labelArea, htmlFor: name, informationIcon: labelInformationIcon, informationText: labelInformationText, labelText: label, optionalMarker: labelOptionalMarker }), jsxs("div", { className: cx(formFieldClasses.dataEntry), children: [children, hintText || hintTextIcon || counter ? (jsxs("div", { className: cx(formFieldClasses.hintTextAndCounterArea, {
24
+ [formFieldClasses.hintTextAndCounterArea + '--align-right']: !(hintText || hintTextIcon) && counter,
25
+ }), children: [(hintText || hintTextIcon) && (jsx(FormHintText, { hintText: hintText, hintTextIcon: hintTextIcon, severity: severity })), counter && (jsx("span", { className: cx(formFieldClasses.counter, formFieldClasses.counterColor(counterColor || FormFieldCounterColor.INFO)), children: counter }))] })) : null] })] }) }));
22
26
  });
23
27
 
24
28
  export { FormField as default };
@@ -1,17 +1,40 @@
1
1
  import { formHintIcons } from '@mezzanine-ui/core/form';
2
2
  import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
3
+ import { IconDefinition } from '@mezzanine-ui/icons';
3
4
  export type FormHintTextProps = NativeElementPropsWithoutKeyAndRef<'span'> & {
5
+ /**
6
+ * The hint text to display below the input field.
7
+ * Provides additional information or guidance to the user.
8
+ */
9
+ hintText?: string;
10
+ /**
11
+ * The icon to display alongside the hint text.
12
+ * If provided, this icon will override the default severity icon.
13
+ */
14
+ hintTextIcon?: IconDefinition;
4
15
  /**
5
16
  * The severity of form message.
17
+ * if not provided, no icon will be displayed.
6
18
  */
7
19
  severity?: keyof typeof formHintIcons | undefined;
8
20
  };
9
21
  /**
10
- * The react component for `mezzanine` form message.
22
+ * The React component for `mezzanine` form message.
11
23
  */
12
24
  declare const FormHintText: import("react").ForwardRefExoticComponent<NativeElementPropsWithoutKeyAndRef<"span"> & {
25
+ /**
26
+ * The hint text to display below the input field.
27
+ * Provides additional information or guidance to the user.
28
+ */
29
+ hintText?: string;
30
+ /**
31
+ * The icon to display alongside the hint text.
32
+ * If provided, this icon will override the default severity icon.
33
+ */
34
+ hintTextIcon?: IconDefinition;
13
35
  /**
14
36
  * The severity of form message.
37
+ * if not provided, no icon will be displayed.
15
38
  */
16
39
  severity?: keyof typeof formHintIcons | undefined;
17
40
  } & import("react").RefAttributes<HTMLSpanElement>>;
@@ -6,12 +6,12 @@ import Icon from '../Icon/Icon.js';
6
6
  import cx from 'clsx';
7
7
 
8
8
  /**
9
- * The react component for `mezzanine` form message.
9
+ * The React component for `mezzanine` form message.
10
10
  */
11
11
  const FormHintText = forwardRef(function FormHintText(props, ref) {
12
- const { children, className, severity, ...rest } = props;
13
- const icon = severity ? formHintIcons[severity] : null;
14
- return (jsxs("span", { ...rest, ref: ref, className: cx(formFieldClasses.hintText, severity ? formFieldClasses.hintTextSeverity(severity) : undefined, className), children: [icon && jsx(Icon, { className: formFieldClasses.hintTextIcon, icon: icon }), children] }));
12
+ const { className, hintText, hintTextIcon, severity = 'info', ...rest } = props;
13
+ const defaultIcon = severity ? formHintIcons[severity] : null;
14
+ return (jsxs("span", { ...rest, ref: ref, className: cx(formFieldClasses.hintText, severity ? formFieldClasses.hintTextSeverity(severity) : undefined, className), children: [hintTextIcon ? (jsx(Icon, { className: formFieldClasses.hintTextIcon, icon: hintTextIcon, color: severity })) : (defaultIcon && (jsx(Icon, { className: formFieldClasses.hintTextIcon, icon: defaultIcon, color: severity }))), hintText] }));
15
15
  });
16
16
 
17
17
  export { FormHintText as default };
@@ -1,11 +1,14 @@
1
1
  import { ReactNode } from 'react';
2
2
  import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
3
+ import { IconDefinition } from '@mezzanine-ui/icons';
3
4
  export interface FormLabelProps extends NativeElementPropsWithoutKeyAndRef<'label'> {
4
- remark?: ReactNode;
5
- remarkIcon?: ReactNode;
5
+ informationIcon?: IconDefinition;
6
+ informationText?: string;
7
+ labelText: string;
8
+ optionalMarker?: ReactNode;
6
9
  }
7
10
  /**
8
- * The react component for `mezzanine` form label.
11
+ * The React component for `mezzanine` form label.
9
12
  */
10
13
  declare const FormLabel: import("react").ForwardRefExoticComponent<FormLabelProps & import("react").RefAttributes<HTMLLabelElement>>;
11
14
  export default FormLabel;
package/Form/FormLabel.js CHANGED
@@ -3,15 +3,17 @@ import { jsxs, jsx } from 'react/jsx-runtime';
3
3
  import { forwardRef, useContext } from 'react';
4
4
  import { formFieldClasses } from '@mezzanine-ui/core/form';
5
5
  import { FormControlContext } from './FormControlContext.js';
6
+ import Tooltip from '../Tooltip/Tooltip.js';
7
+ import Icon from '../Icon/Icon.js';
6
8
  import cx from 'clsx';
7
9
 
8
10
  /**
9
- * The react component for `mezzanine` form label.
11
+ * The React component for `mezzanine` form label.
10
12
  */
11
13
  const FormLabel = forwardRef(function FormLabel(props, ref) {
12
- const { children, className, htmlFor, remark, remarkIcon, ...rest } = props;
14
+ const { className, htmlFor, informationIcon, informationText, labelText, optionalMarker, ...rest } = props;
13
15
  const { required } = useContext(FormControlContext) || {};
14
- return (jsxs("label", { ...rest, ref: ref, className: cx(formFieldClasses.label, className), htmlFor: htmlFor, children: [jsxs("span", { children: [children, required && jsx("span", { className: formFieldClasses.asterisk, children: "*" })] }), (remark || remarkIcon) && (jsxs("span", { className: formFieldClasses.remark, children: [jsx("span", { children: remark }), remarkIcon] }))] }));
16
+ return (jsxs("label", { ...rest, ref: ref, className: cx(formFieldClasses.label, className), htmlFor: htmlFor, children: [required && jsx("span", { className: formFieldClasses.labelRequiredMarker, children: "*" }), labelText, optionalMarker && (jsx("span", { className: formFieldClasses.labelOptionalMarker, children: optionalMarker })), informationIcon && (jsx(Tooltip, { title: informationText, children: ({ onMouseEnter, onMouseLeave, ref: tooltipRef }) => (jsx(Icon, { ref: tooltipRef, className: cx(formFieldClasses.labelInformationIcon), color: "neutral-light", icon: informationIcon, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, size: 16 })) })), jsx("span", { className: formFieldClasses.labelColon, children: ":" })] }));
15
17
  });
16
18
 
17
19
  export { FormLabel as default };