@mezzanine-ui/react 1.0.0-canary.2 → 1.0.0-canary.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 (113) hide show
  1. package/AlertBanner/AlertBanner.d.ts +93 -0
  2. package/AlertBanner/AlertBanner.js +135 -0
  3. package/AlertBanner/index.d.ts +2 -0
  4. package/AlertBanner/index.js +1 -0
  5. package/{Overlay/Overlay.d.ts → Backdrop/Backdrop.d.ts} +12 -16
  6. package/Backdrop/Backdrop.js +33 -0
  7. package/Backdrop/index.d.ts +2 -0
  8. package/Backdrop/index.js +1 -0
  9. package/Badge/Badge.d.ts +20 -14
  10. package/Badge/Badge.js +14 -16
  11. package/Badge/BadgeContainer.d.ts +1 -1
  12. package/Badge/BadgeContainer.js +1 -1
  13. package/Badge/index.d.ts +1 -2
  14. package/Badge/typings.d.ts +42 -0
  15. package/Button/Button.js +2 -2
  16. package/Calendar/CalendarContext.js +1 -1
  17. package/DismissButton/DismissButton.d.ts +34 -0
  18. package/DismissButton/DismissButton.js +19 -0
  19. package/DismissButton/index.d.ts +3 -0
  20. package/DismissButton/index.js +5 -0
  21. package/Drawer/Drawer.js +2 -2
  22. package/Dropdown/Dropdown.js +12 -22
  23. package/InlineMessage/InlineMessage.d.ts +33 -0
  24. package/InlineMessage/InlineMessage.js +37 -0
  25. package/InlineMessage/InlineMessageGroup.d.ts +24 -0
  26. package/InlineMessage/InlineMessageGroup.js +21 -0
  27. package/InlineMessage/index.d.ts +4 -0
  28. package/InlineMessage/index.js +2 -0
  29. package/Message/Message.d.ts +4 -2
  30. package/Message/Message.js +85 -8
  31. package/Message/MessageTimerController.d.ts +14 -0
  32. package/Message/MessageTimerController.js +27 -0
  33. package/Modal/Modal.js +2 -2
  34. package/Modal/useModalContainer.d.ts +1 -1
  35. package/Modal/useModalContainer.js +2 -4
  36. package/Navigation/NavigationSubMenu.js +15 -26
  37. package/Notifier/NotifierManager.d.ts +15 -2
  38. package/Notifier/NotifierManager.js +60 -18
  39. package/Notifier/createNotifier.d.ts +9 -0
  40. package/Notifier/createNotifier.js +3 -2
  41. package/Notifier/typings.d.ts +3 -1
  42. package/PageFooter/PageFooter.d.ts +58 -8
  43. package/PageFooter/PageFooter.js +34 -3
  44. package/Popover/Popover.js +4 -8
  45. package/Popper/Popper.d.ts +15 -10
  46. package/Popper/Popper.js +88 -17
  47. package/Popper/index.d.ts +1 -1
  48. package/Portal/Portal.d.ts +13 -5
  49. package/Portal/Portal.js +25 -10
  50. package/Portal/index.d.ts +2 -0
  51. package/Portal/index.js +1 -0
  52. package/Portal/portalRegistry.d.ts +17 -0
  53. package/Portal/portalRegistry.js +99 -0
  54. package/Provider/ConfigProvider.d.ts +1 -0
  55. package/Provider/ConfigProvider.js +1 -0
  56. package/ResultState/ResultState.d.ts +36 -0
  57. package/ResultState/ResultState.js +24 -0
  58. package/ResultState/index.d.ts +2 -0
  59. package/ResultState/index.js +1 -0
  60. package/Skeleton/Skeleton.d.ts +10 -3
  61. package/Skeleton/Skeleton.js +12 -4
  62. package/Spin/Spin.d.ts +39 -0
  63. package/{Loading/Loading.js → Spin/Spin.js} +8 -8
  64. package/Spin/index.d.ts +2 -0
  65. package/Spin/index.js +1 -0
  66. package/Stepper/Step.d.ts +1 -35
  67. package/Stepper/Step.js +43 -10
  68. package/Stepper/Stepper.d.ts +1 -14
  69. package/Stepper/Stepper.js +49 -15
  70. package/Stepper/index.d.ts +3 -2
  71. package/Stepper/index.js +1 -0
  72. package/Stepper/typings.d.ts +77 -0
  73. package/Stepper/useStepDistance.d.ts +10 -0
  74. package/Stepper/useStepDistance.js +81 -0
  75. package/Stepper/useStepper.d.ts +21 -0
  76. package/Stepper/useStepper.js +26 -0
  77. package/Table/Table.js +2 -2
  78. package/Table/TableBody.js +2 -2
  79. package/Tag/Tag.d.ts +2 -25
  80. package/Tag/Tag.js +14 -13
  81. package/Tag/index.d.ts +1 -1
  82. package/Tag/typings.d.ts +79 -0
  83. package/Toggle/Toggle.d.ts +46 -0
  84. package/Toggle/Toggle.js +29 -0
  85. package/Toggle/index.d.ts +3 -0
  86. package/Toggle/index.js +1 -0
  87. package/Tooltip/Tooltip.d.ts +13 -2
  88. package/Tooltip/Tooltip.js +48 -10
  89. package/Tooltip/useDelayMouseEnterLeave.d.ts +1 -2
  90. package/Tooltip/useDelayMouseEnterLeave.js +0 -4
  91. package/Tree/TreeNode.js +5 -5
  92. package/_internal/InputTriggerPopper/InputTriggerPopper.js +14 -24
  93. package/_internal/SlideFadeOverlay/SlideFadeOverlay.d.ts +2 -2
  94. package/_internal/SlideFadeOverlay/SlideFadeOverlay.js +4 -27
  95. package/hooks/useScrollLock.d.ts +28 -0
  96. package/hooks/useScrollLock.js +134 -0
  97. package/index.d.ts +87 -80
  98. package/index.js +74 -69
  99. package/package.json +7 -7
  100. package/utils/get-css-variable-value.d.ts +1 -0
  101. package/utils/get-css-variable-value.js +9 -0
  102. package/Loading/Loading.d.ts +0 -33
  103. package/Loading/index.d.ts +0 -2
  104. package/Loading/index.js +0 -1
  105. package/Overlay/Overlay.js +0 -31
  106. package/Overlay/index.d.ts +0 -2
  107. package/Overlay/index.js +0 -1
  108. package/Switch/Switch.d.ts +0 -43
  109. package/Switch/Switch.js +0 -29
  110. package/Switch/index.d.ts +0 -3
  111. package/Switch/index.js +0 -1
  112. package/utils/scroll-lock.d.ts +0 -2
  113. package/utils/scroll-lock.js +0 -24
@@ -1,15 +1,65 @@
1
- import { ReactNode } from 'react';
2
- import { ConfirmActionsProps } from '../ConfirmActions';
3
1
  import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
4
- export interface PageFooterProps extends NativeElementPropsWithoutKeyAndRef<'footer'>, Pick<ConfirmActionsProps, 'cancelButtonProps' | 'cancelText' | 'confirmButtonProps' | 'confirmText' | 'danger' | 'loading' | 'hideCancelButton' | 'hideConfirmButton' | 'onCancel' | 'onConfirm'> {
2
+ import type { ButtonProps } from '../Button';
3
+ export type PageFooterType = 'standard' | 'overflow' | 'information';
4
+ type PageFooterBaseProps = NativeElementPropsWithoutKeyAndRef<'footer'> & {
5
5
  /**
6
- * The className of action wrapper.
6
+ * Action buttons configuration for primary and secondary actions.
7
+ * Renders buttons in the order: secondary (left), primary (right).
7
8
  */
8
- actionClassName?: string;
9
+ actions?: {
10
+ primaryButtonProps?: ButtonProps;
11
+ secondaryButtonProps?: ButtonProps;
12
+ };
9
13
  /**
10
- * The action element in the left.
14
+ * The className of annotation wrapper.
11
15
  */
12
- children?: ReactNode;
13
- }
16
+ annotationClassName?: string;
17
+ /**
18
+ * The warning message in the middle.
19
+ */
20
+ warningMessage?: string;
21
+ };
22
+ type PageFooterStandardProps = PageFooterBaseProps & {
23
+ /**
24
+ * The type of PageFooter annotation.
25
+ * @default 'standard'
26
+ */
27
+ type?: 'standard';
28
+ /**
29
+ * Standard type: A ghost button with text.
30
+ * Children of the button.
31
+ */
32
+ annotation?: string;
33
+ /**
34
+ * Standard type: Button click handler.
35
+ */
36
+ onAnnotationClick?: ButtonProps['onClick'];
37
+ };
38
+ type PageFooterOverflowProps = PageFooterBaseProps & {
39
+ /**
40
+ * The type of PageFooter annotation.
41
+ */
42
+ type: 'overflow';
43
+ /**
44
+ * Overflow type: Icon for the icon-only button.
45
+ * @TODO Consider Dropdown integration after Dropdown redesign.
46
+ */
47
+ annotation?: ButtonProps['icon'];
48
+ /**
49
+ * Overflow type: Button click handler.
50
+ */
51
+ onAnnotationClick?: ButtonProps['onClick'];
52
+ };
53
+ type PageFooterInformationProps = PageFooterBaseProps & {
54
+ /**
55
+ * The type of PageFooter annotation.
56
+ */
57
+ type: 'information';
58
+ /**
59
+ * Information type: Plain text to display.
60
+ */
61
+ annotation?: string;
62
+ };
63
+ export type PageFooterProps = PageFooterStandardProps | PageFooterOverflowProps | PageFooterInformationProps;
14
64
  declare const PageFooter: import("react").ForwardRefExoticComponent<PageFooterProps & import("react").RefAttributes<HTMLElement>>;
15
65
  export default PageFooter;
@@ -1,12 +1,43 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import { forwardRef } from 'react';
3
3
  import { pageFooterClasses } from '@mezzanine-ui/core/page-footer';
4
- import ConfirmActions from '../ConfirmActions/ConfirmActions.js';
4
+ import Button from '../Button/Button.js';
5
+ import ButtonGroup from '../Button/ButtonGroup.js';
6
+ import Typography from '../Typography/Typography.js';
5
7
  import cx from 'clsx';
6
8
 
7
9
  const PageFooter = forwardRef(function PageFooter(props, ref) {
8
- const { className, actionClassName, cancelButtonProps, cancelText, children, danger, loading, confirmButtonProps, confirmText, hideCancelButton, hideConfirmButton, onCancel, onConfirm, ...rest } = props;
9
- return (jsxs("footer", { ref: ref, ...rest, className: cx(pageFooterClasses.host, className), children: [jsx("div", { className: actionClassName, children: children }), jsx(ConfirmActions, { cancelText: cancelText, confirmText: confirmText, cancelButtonProps: cancelButtonProps, confirmButtonProps: confirmButtonProps, danger: danger, hideCancelButton: hideCancelButton, hideConfirmButton: hideConfirmButton, loading: loading, onCancel: onCancel, onConfirm: onConfirm })] }));
10
+ var _a;
11
+ const { actions, annotation, annotationClassName, className, type = 'standard', warningMessage, ...rest } = props;
12
+ // Filter out onAnnotationClick from rest props to avoid React warnings
13
+ if ('onAnnotationClick' in rest) {
14
+ delete rest.onAnnotationClick;
15
+ }
16
+ const { children: primaryButtonText, ...restPrimaryButtonProps } = (_a = actions === null || actions === void 0 ? void 0 : actions.primaryButtonProps) !== null && _a !== void 0 ? _a : {};
17
+ // Render annotation based on type
18
+ const renderAnnotation = () => {
19
+ if (!annotation)
20
+ return null;
21
+ switch (type) {
22
+ case 'standard': {
23
+ return (jsx(Button, { onClick: 'onAnnotationClick' in props
24
+ ? props.onAnnotationClick
25
+ : undefined, size: "main", variant: "base-ghost", children: annotation }));
26
+ }
27
+ case 'overflow': {
28
+ // @TODO Consider Dropdown integration after Dropdown redesign
29
+ return (jsx(Button, { icon: annotation, onClick: 'onAnnotationClick' in props
30
+ ? props.onAnnotationClick
31
+ : undefined, size: "main", variant: "base-ghost" }));
32
+ }
33
+ case 'information': {
34
+ return (jsx(Typography, { color: "text-neutral", variant: "caption", children: annotation }));
35
+ }
36
+ default:
37
+ return null;
38
+ }
39
+ };
40
+ return (jsxs("footer", { ref: ref, ...rest, className: cx(pageFooterClasses.host, className), children: [jsx("div", { className: cx(pageFooterClasses.annotation, annotationClassName), children: renderAnnotation() }), jsx("div", { className: pageFooterClasses.message, children: warningMessage ? (jsx(Typography, { variant: "caption", color: "text-warning", align: "right", children: warningMessage })) : null }), jsxs(ButtonGroup, { children: [(actions === null || actions === void 0 ? void 0 : actions.secondaryButtonProps) && (jsx(Button, { size: "main", variant: "base-secondary", ...actions.secondaryButtonProps })), jsx(Button, { size: "main", variant: "base-primary", ...restPrimaryButtonProps, children: primaryButtonText || 'Button' })] })] }));
10
41
  });
11
42
 
12
43
  export { PageFooter as default };
@@ -1,23 +1,19 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import { forwardRef, useRef } from 'react';
3
3
  import { popoverClasses } from '@mezzanine-ui/core/popover';
4
+ import { offset } from '@floating-ui/react-dom';
4
5
  import { useClickAway } from '../hooks/useClickAway.js';
5
6
  import { useComposeRefs } from '../hooks/useComposeRefs.js';
6
7
  import Popper from '../Popper/Popper.js';
7
8
  import cx from 'clsx';
8
9
 
9
- const offsetModifier = {
10
- name: 'offset',
11
- options: {
12
- offset: [0, 8],
13
- },
14
- };
10
+ const offsetMiddleware = offset({ mainAxis: 8 });
15
11
  /**
16
12
  * The react component for `mezzanine` popover.
17
13
  */
18
14
  const Popover = forwardRef(function Popover(props, ref) {
19
15
  const { children, className, disableClickAway = false, onClose, open, options = {}, title, ...rest } = props;
20
- const { modifiers = [] } = options;
16
+ const { middleware = [] } = options;
21
17
  const popoverRef = useRef(null);
22
18
  const composedRef = useComposeRefs([ref, popoverRef]);
23
19
  useClickAway(() => {
@@ -32,7 +28,7 @@ const Popover = forwardRef(function Popover(props, ref) {
32
28
  }, popoverRef, [open, disableClickAway, onClose, popoverRef]);
33
29
  return (jsxs(Popper, { ...rest, ref: composedRef, className: cx(popoverClasses.host, className), open: open, options: {
34
30
  ...options,
35
- modifiers: [offsetModifier, ...modifiers],
31
+ middleware: [offsetMiddleware, ...middleware],
36
32
  }, children: [title && jsx("div", { className: popoverClasses.title, children: title }), children && jsx("div", { className: popoverClasses.content, children: children })] }));
37
33
  });
38
34
 
@@ -1,21 +1,26 @@
1
- import { Options as _PopperOptions } from '@popperjs/core';
2
1
  import { Ref } from 'react';
3
- import { Modifier, usePopper } from 'react-popper';
2
+ import { UseFloatingOptions, UseFloatingReturn, Placement, Strategy } from '@floating-ui/react-dom';
4
3
  import { ElementGetter } from '../utils/getElement';
5
4
  import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
6
5
  import { PortalProps } from '../Portal';
7
- export type { Placement as PopperPlacement, PositioningStrategy as PopperPositionStrategy, } from '@popperjs/core';
8
- export type PopperOptions<Modifiers> = Omit<Partial<_PopperOptions>, 'modifiers'> & {
9
- modifiers?: ReadonlyArray<Modifier<Modifiers>>;
10
- };
11
- export type PopperController = ReturnType<typeof usePopper>;
6
+ export type PopperPlacement = Placement;
7
+ export type PopperPositionStrategy = Strategy;
8
+ export type PopperController = UseFloatingReturn;
12
9
  export interface PopperProps extends Pick<PortalProps, 'container' | 'disablePortal'>, NativeElementPropsWithoutKeyAndRef<'div'> {
13
10
  /**
14
11
  * The ref of trigger Element.
15
12
  */
16
13
  anchor?: ElementGetter;
17
14
  /**
18
- * Provide `controllerRef` if you need access to `usePopper` results.
15
+ * Whether to show arrow element on the popper
16
+ */
17
+ arrow?: {
18
+ enabled: boolean;
19
+ className: string;
20
+ padding?: number;
21
+ } | null;
22
+ /**
23
+ * Provide `controllerRef` if you need access to `useFloating` results.
19
24
  */
20
25
  controllerRef?: Ref<PopperController>;
21
26
  /**
@@ -24,9 +29,9 @@ export interface PopperProps extends Pick<PortalProps, 'container' | 'disablePor
24
29
  */
25
30
  open?: boolean;
26
31
  /**
27
- * The options of usePopper hook of react-popper.
32
+ * The options of useFloating hook from @floating-ui/react-dom.
28
33
  */
29
- options?: PopperOptions<any>;
34
+ options?: UseFloatingOptions;
30
35
  }
31
36
  declare const Popper: import("react").ForwardRefExoticComponent<PopperProps & import("react").RefAttributes<HTMLDivElement>>;
32
37
  export default Popper;
package/Popper/Popper.js CHANGED
@@ -1,30 +1,101 @@
1
- import { jsx } from 'react/jsx-runtime';
2
- import { forwardRef, useState, useImperativeHandle } from 'react';
3
- import { usePopper } from 'react-popper';
1
+ 'use client';
2
+ import { jsx, jsxs } from 'react/jsx-runtime';
3
+ import { forwardRef, useRef, useImperativeHandle, useEffect, useCallback } from 'react';
4
+ import { useFloating, arrow } from '@floating-ui/react-dom';
5
+ import { spacingPrefix } from '@mezzanine-ui/system/spacing';
4
6
  import { getElement } from '../utils/getElement.js';
5
7
  import { useComposeRefs } from '../hooks/useComposeRefs.js';
8
+ import { getCSSVariableValue } from '../utils/get-css-variable-value.js';
6
9
  import Portal from '../Portal/Portal.js';
7
10
 
8
11
  const Popper = forwardRef(function Popper(props, ref) {
9
- const { anchor, children, container, controllerRef, disablePortal, open = false, options, style, ...rest } = props;
10
- const [popperEl, setPopperEl] = useState(null);
11
- const composedRef = useComposeRefs([ref, setPopperEl]);
12
+ var _a, _b, _c, _d;
13
+ const { anchor, arrow: arrow$1, children, container, controllerRef, disablePortal, open = false, options, style, ...rest } = props;
14
+ const arrowRef = useRef(null);
12
15
  const anchorEl = getElement(anchor);
13
- const { attributes, forceUpdate, state, styles, update } = usePopper(anchorEl, popperEl, options);
14
- useImperativeHandle(controllerRef, () => ({
15
- attributes,
16
- forceUpdate,
17
- state,
18
- styles,
19
- update,
20
- }));
16
+ const floatingReturn = useFloating({
17
+ ...options,
18
+ middleware: [
19
+ ...((_a = options === null || options === void 0 ? void 0 : options.middleware) !== null && _a !== void 0 ? _a : []),
20
+ (arrow$1 === null || arrow$1 === void 0 ? void 0 : arrow$1.enabled)
21
+ ? arrow({
22
+ element: arrowRef.current,
23
+ padding: arrow$1.padding || 0,
24
+ })
25
+ : null,
26
+ ],
27
+ elements: {
28
+ reference: anchorEl,
29
+ },
30
+ });
31
+ const { refs, floatingStyles, update } = floatingReturn;
32
+ const composedRef = useComposeRefs([ref, refs.setFloating]);
33
+ useImperativeHandle(controllerRef, () => floatingReturn, [floatingReturn]);
34
+ useEffect(() => {
35
+ if (anchorEl) {
36
+ refs.setReference(anchorEl);
37
+ }
38
+ }, [anchorEl, refs]);
39
+ useEffect(() => {
40
+ if (open && update) {
41
+ update();
42
+ }
43
+ }, [open, update]);
44
+ // 計算箭頭的位置和旋轉角度
45
+ const arrowX = (_b = floatingReturn.middlewareData.arrow) === null || _b === void 0 ? void 0 : _b.x;
46
+ const arrowY = (_c = floatingReturn.middlewareData.arrow) === null || _c === void 0 ? void 0 : _c.y;
47
+ const placement = floatingReturn.placement;
48
+ // 根據 placement 決定箭頭的靜態位置和旋轉
49
+ const getArrowStyles = useCallback(() => {
50
+ const staticSide = {
51
+ top: {
52
+ property: 'bottom',
53
+ value: '-6px',
54
+ },
55
+ right: {
56
+ property: 'left',
57
+ value: '-8px',
58
+ },
59
+ bottom: {
60
+ property: 'top',
61
+ value: '-6px',
62
+ },
63
+ left: {
64
+ property: 'right',
65
+ value: '-8px',
66
+ },
67
+ }[placement.split('-')[0]];
68
+ const arrowStyles = {
69
+ width: getCSSVariableValue(`--${spacingPrefix}-size-element-slim`),
70
+ height: getCSSVariableValue(`--${spacingPrefix}-size-element-tight`),
71
+ transformOrigin: 'center',
72
+ position: 'absolute',
73
+ left: typeof arrowX === 'number' ? `${arrowX}px` : undefined,
74
+ top: typeof arrowY === 'number' ? `${arrowY}px` : undefined,
75
+ [staticSide.property]: staticSide.value,
76
+ };
77
+ // 根據 placement 旋轉箭頭
78
+ const rotation = {
79
+ top: '0deg',
80
+ right: '90deg',
81
+ bottom: '180deg',
82
+ left: '-90deg',
83
+ }[placement.split('-')[0]];
84
+ if (rotation) {
85
+ arrowStyles.transform = `rotate(${rotation})`;
86
+ }
87
+ return arrowStyles;
88
+ }, [arrowX, arrowY, placement]);
21
89
  if (!open) {
22
90
  return null;
23
91
  }
24
- return (jsx(Portal, { container: container, disablePortal: disablePortal, children: jsx("div", { ...rest, ref: composedRef, style: {
92
+ return (jsx(Portal, { container: container, disablePortal: disablePortal, children: jsxs("div", { ...rest, ref: composedRef, "data-popper-placement": floatingReturn.placement, style: {
25
93
  ...style,
26
- ...styles.popper,
27
- }, ...attributes.popper, children: children }) }));
94
+ ...floatingStyles,
95
+ visibility: ((_d = floatingReturn.middlewareData.hide) === null || _d === void 0 ? void 0 : _d.referenceHidden)
96
+ ? 'hidden'
97
+ : 'visible',
98
+ }, children: [(arrow$1 === null || arrow$1 === void 0 ? void 0 : arrow$1.enabled) ? (jsx("svg", { viewBox: "0 0 12 6", fill: "none", ref: arrowRef, className: arrow$1 === null || arrow$1 === void 0 ? void 0 : arrow$1.className, style: getArrowStyles(), children: jsx("path", { d: "M6.70711 5.29289C6.31658 5.68342 5.68342 5.68342 5.29289 5.29289L0 0L12 6.05683e-07L6.70711 5.29289Z", fill: "currentColor" }) })) : null, children] }) }));
28
99
  });
29
100
 
30
101
  export { Popper as default };
package/Popper/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  export { default } from './Popper';
2
- export type { PopperPlacement, PopperPositionStrategy, PopperOptions, PopperController, PopperProps, } from './Popper';
2
+ export type { PopperPlacement, PopperPositionStrategy, PopperController, PopperProps, } from './Popper';
@@ -1,5 +1,5 @@
1
- import { FC, ReactNode } from 'react';
2
- import { ElementGetter } from '../utils/getElement';
1
+ import { ReactNode, RefObject } from 'react';
2
+ import { type PortalLayer } from './portalRegistry';
3
3
  export interface PortalProps {
4
4
  /**
5
5
  * The element you want to portal.
@@ -7,14 +7,22 @@ export interface PortalProps {
7
7
  children?: ReactNode;
8
8
  /**
9
9
  * The destination where to portal.
10
+ * If provided, it will override the default portal container.
11
+ * Accepts HTMLElement, RefObject, or null.
10
12
  */
11
- container?: ElementGetter;
13
+ container?: HTMLElement | RefObject<HTMLElement | null> | RefObject<HTMLElement> | null;
12
14
  /**
13
15
  * Whether to disable portal.
14
16
  * If true, it will be a normal component.
15
17
  * @default false
16
18
  */
17
19
  disablePortal?: boolean;
20
+ /**
21
+ * The portal layer to use.
22
+ * - 'alert': Portal to alert container (above root, sticky)
23
+ * - 'default': Portal to default container (inside root, fixed)
24
+ * @default 'default'
25
+ */
26
+ layer?: PortalLayer;
18
27
  }
19
- declare const Portal: FC<PortalProps>;
20
- export default Portal;
28
+ export default function Portal({ children, container: containerProp, disablePortal, layer, }: PortalProps): import("react/jsx-runtime").JSX.Element | null;
package/Portal/Portal.js CHANGED
@@ -1,23 +1,38 @@
1
+ 'use client';
1
2
  import { jsx, Fragment } from 'react/jsx-runtime';
2
3
  import { useState, useEffect } from 'react';
3
4
  import { createPortal } from 'react-dom';
4
- import { getElement } from '../utils/getElement.js';
5
+ import { getContainer } from './portalRegistry.js';
5
6
 
6
- const Portal = (props) => {
7
- const { children, container, disablePortal = false } = props;
8
- const [portalElement, setPortalElement] = useState(() => disablePortal ? null : getElement(container));
7
+ function Portal({ children, container: containerProp, disablePortal = false, layer = 'default', }) {
8
+ const [container, setContainer] = useState(null);
9
+ // Extract ref.current for dependency tracking
10
+ const refCurrent = containerProp && 'current' in containerProp
11
+ ? containerProp.current
12
+ : undefined;
9
13
  useEffect(() => {
10
- if (!disablePortal) {
11
- setPortalElement(getElement(container) || document.body);
14
+ if (disablePortal)
15
+ return;
16
+ if (containerProp) {
17
+ // Handle RefObject
18
+ if ('current' in containerProp) {
19
+ setContainer(containerProp.current);
20
+ return;
21
+ }
22
+ // Handle HTMLElement
23
+ setContainer(containerProp);
24
+ return;
12
25
  }
13
- }, [container, disablePortal]);
26
+ const targetContainer = getContainer(layer);
27
+ setContainer(targetContainer);
28
+ }, [containerProp, disablePortal, layer, refCurrent]);
14
29
  /** This element is fully client side, so `return null` on server side */
15
30
  if (typeof window === 'undefined')
16
31
  return null;
17
- if (disablePortal || !portalElement) {
32
+ if (disablePortal || !container) {
18
33
  return jsx(Fragment, { children: children });
19
34
  }
20
- return createPortal(children, portalElement);
21
- };
35
+ return createPortal(children, container);
36
+ }
22
37
 
23
38
  export { Portal as default };
package/Portal/index.d.ts CHANGED
@@ -1,2 +1,4 @@
1
1
  export type { PortalProps } from './Portal';
2
+ export type { PortalLayer } from './portalRegistry';
2
3
  export { default } from './Portal';
4
+ export { getContainer, getRootElement, initializePortals, resetPortals, } from './portalRegistry';
package/Portal/index.js CHANGED
@@ -1 +1,2 @@
1
1
  export { default } from './Portal.js';
2
+ export { getContainer, getRootElement, initializePortals, resetPortals } from './portalRegistry.js';
@@ -0,0 +1,17 @@
1
+ export type PortalLayer = 'alert' | 'default';
2
+ /**
3
+ * Initialize portal containers.
4
+ * This function can be called manually in your app's entry point,
5
+ * or it will be called automatically when the first Portal is rendered.
6
+ *
7
+ * @param rootSelector - Optional root element or selector string.
8
+ * Defaults to document.body
9
+ */
10
+ export declare function initializePortals(rootSelector?: string | HTMLElement): void;
11
+ export declare function getContainer(layer: PortalLayer): HTMLElement | null;
12
+ export declare function getRootElement(): HTMLElement | null;
13
+ /**
14
+ * Reset portal system state. Only for testing purposes.
15
+ * @internal
16
+ */
17
+ export declare function resetPortals(): void;
@@ -0,0 +1,99 @@
1
+ // Module-level state
2
+ let containers = {
3
+ alert: null,
4
+ default: null,
5
+ };
6
+ let rootElement = null;
7
+ let initialized = false;
8
+ let alertHeight = 0;
9
+ function findRootElement(rootProp) {
10
+ if (rootProp instanceof HTMLElement)
11
+ return rootProp;
12
+ if (typeof rootProp === 'string') {
13
+ const element = document.querySelector(rootProp);
14
+ if (element instanceof HTMLElement)
15
+ return element;
16
+ }
17
+ return document.body;
18
+ }
19
+ function observeAlertHeight() {
20
+ if (!containers.alert || !containers.default)
21
+ return;
22
+ const updatePortalTop = () => {
23
+ if (!containers.alert || !containers.default)
24
+ return;
25
+ const height = containers.alert.offsetHeight;
26
+ if (height !== alertHeight) {
27
+ alertHeight = height;
28
+ containers.default.style.top = `${height}px`;
29
+ containers.default.style.height = `calc(100% - ${height}px)`;
30
+ }
31
+ };
32
+ // Use ResizeObserver to track alert container height changes
33
+ const observer = new ResizeObserver(updatePortalTop);
34
+ observer.observe(containers.alert);
35
+ // Initial update
36
+ updatePortalTop();
37
+ }
38
+ function createContainers() {
39
+ if (!rootElement)
40
+ return;
41
+ const alertContainer = document.createElement('div');
42
+ alertContainer.id = 'mzn-alert-container';
43
+ alertContainer.className = 'mzn-portal-alert';
44
+ const portalContainer = document.createElement('div');
45
+ portalContainer.id = 'mzn-portal-container';
46
+ portalContainer.className = 'mzn-portal-default';
47
+ rootElement.insertBefore(alertContainer, rootElement.firstChild);
48
+ rootElement.appendChild(portalContainer);
49
+ containers = {
50
+ alert: alertContainer,
51
+ default: portalContainer,
52
+ };
53
+ // Observe alert height changes and update portal container position
54
+ observeAlertHeight();
55
+ }
56
+ /**
57
+ * Initialize portal containers.
58
+ * This function can be called manually in your app's entry point,
59
+ * or it will be called automatically when the first Portal is rendered.
60
+ *
61
+ * @param rootSelector - Optional root element or selector string.
62
+ * Defaults to document.body
63
+ */
64
+ function initializePortals(rootSelector) {
65
+ if (initialized || typeof window === 'undefined')
66
+ return;
67
+ initialized = true;
68
+ rootElement = findRootElement(rootSelector);
69
+ if (!rootElement)
70
+ return;
71
+ createContainers();
72
+ }
73
+ function getContainer(layer) {
74
+ if (!initialized && typeof window !== 'undefined') {
75
+ initializePortals();
76
+ }
77
+ return containers[layer];
78
+ }
79
+ function getRootElement() {
80
+ if (!initialized && typeof window !== 'undefined') {
81
+ initializePortals();
82
+ }
83
+ return rootElement;
84
+ }
85
+ /**
86
+ * Reset portal system state. Only for testing purposes.
87
+ * @internal
88
+ */
89
+ function resetPortals() {
90
+ containers = {
91
+ alert: null,
92
+ default: null,
93
+ };
94
+ rootElement = null;
95
+ initialized = false;
96
+ alertHeight = 0;
97
+ }
98
+
99
+ export { getContainer, getRootElement, initializePortals, resetPortals };
@@ -4,5 +4,6 @@ export interface ConfigProviderProps {
4
4
  children?: ReactNode;
5
5
  size?: MezzanineConfigContext['size'];
6
6
  }
7
+ /** @deprecated 未來不提供此設定 */
7
8
  declare function ConfigProvider(props: ConfigProviderProps): JSX.Element;
8
9
  export default ConfigProvider;
@@ -2,6 +2,7 @@ import { jsx } from 'react/jsx-runtime';
2
2
  import { useMemo } from 'react';
3
3
  import { MezzanineConfig } from './context.js';
4
4
 
5
+ /** @deprecated 未來不提供此設定 */
5
6
  function ConfigProvider(props) {
6
7
  const { children, size } = props;
7
8
  const context = useMemo(() => ({
@@ -0,0 +1,36 @@
1
+ import type { ResultStateSize, ResultStateType } from '@mezzanine-ui/core/result-state';
2
+ import type { ButtonProps } from '../Button';
3
+ import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
4
+ export interface ResultStateProps extends NativeElementPropsWithoutKeyAndRef<'div'> {
5
+ /**
6
+ * Action buttons configuration for primary and secondary actions.
7
+ * Renders buttons in the order: secondary (left), primary (right).
8
+ */
9
+ actions?: {
10
+ primaryButtonProps?: ButtonProps;
11
+ secondaryButtonProps?: ButtonProps;
12
+ };
13
+ /**
14
+ * Optional description text displayed below the title.
15
+ * Provides additional context or details about the result state.
16
+ */
17
+ description?: string;
18
+ /**
19
+ * The size variant of the result state.
20
+ * Controls typography, spacing, and overall dimensions.
21
+ * @default 'main'
22
+ */
23
+ size?: ResultStateSize;
24
+ /**
25
+ * The title text for the result state.
26
+ * This is the main heading that describes the state.
27
+ */
28
+ title: string;
29
+ /**
30
+ * The type of result state, which determines the icon and color theme.
31
+ * @default 'information'
32
+ */
33
+ type?: ResultStateType;
34
+ }
35
+ declare const ResultState: import("react").ForwardRefExoticComponent<ResultStateProps & import("react").RefAttributes<HTMLDivElement>>;
36
+ export default ResultState;
@@ -0,0 +1,24 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { resultStateClasses } from '@mezzanine-ui/core/result-state';
3
+ import { forwardRef, useMemo } from 'react';
4
+ import { DangerousFilledIcon, ErrorFilledIcon, WarningFilledIcon, QuestionFilledIcon, CheckedFilledIcon, InfoFilledIcon } from '@mezzanine-ui/icons';
5
+ import Button from '../Button/Button.js';
6
+ import ButtonGroup from '../Button/ButtonGroup.js';
7
+ import Icon from '../Icon/Icon.js';
8
+ import cx from 'clsx';
9
+
10
+ const iconMap = {
11
+ information: InfoFilledIcon,
12
+ success: CheckedFilledIcon,
13
+ help: QuestionFilledIcon,
14
+ warning: WarningFilledIcon,
15
+ error: ErrorFilledIcon,
16
+ failure: DangerousFilledIcon,
17
+ };
18
+ const ResultState = forwardRef(function ResultState(props, ref) {
19
+ const { actions, className, description, size = 'main', title, type = 'information', ...rest } = props;
20
+ const icon = useMemo(() => iconMap[type], [type]);
21
+ return (jsx("div", { ...rest, ref: ref, className: cx(resultStateClasses.host, resultStateClasses.type(type), resultStateClasses.size(size), className), children: jsxs("div", { className: resultStateClasses.container, children: [jsx(Icon, { className: resultStateClasses.icon, icon: icon }), jsx("h3", { className: resultStateClasses.title, children: title }), description && jsx("p", { className: resultStateClasses.description, children: description }), actions && (jsxs(ButtonGroup, { className: resultStateClasses.actions, children: [actions.secondaryButtonProps && (jsx(Button, { size: "main", variant: "base-secondary", ...actions.secondaryButtonProps })), actions.primaryButtonProps && (jsx(Button, { size: "main", variant: "base-primary", ...actions.primaryButtonProps }))] }))] }) }));
22
+ });
23
+
24
+ export { ResultState as default };
@@ -0,0 +1,2 @@
1
+ export { default } from './ResultState';
2
+ export * from './ResultState';
@@ -0,0 +1 @@
1
+ export { default } from './ResultState.js';