@mezzanine-ui/react 1.0.0-rc.0 → 1.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Badge/Badge.d.ts +4 -0
- package/Badge/Badge.js +2 -2
- package/Badge/typings.d.ts +13 -1
- package/Calendar/CalendarDays.js +4 -3
- package/Calendar/CalendarWeeks.js +4 -3
- package/Cascader/Cascader.d.ts +3 -0
- package/Cascader/Cascader.js +241 -0
- package/Cascader/CascaderPanel.d.ts +29 -0
- package/Cascader/CascaderPanel.js +29 -0
- package/Cascader/index.d.ts +5 -0
- package/Cascader/index.js +2 -0
- package/Cascader/typings.d.ts +92 -0
- package/Description/Description.d.ts +6 -1
- package/Description/Description.js +11 -4
- package/Description/DescriptionContent.d.ts +9 -3
- package/Description/DescriptionContent.js +4 -1
- package/Description/DescriptionContext.d.ts +6 -0
- package/Description/DescriptionContext.js +9 -0
- package/Description/index.d.ts +2 -0
- package/Description/index.js +1 -0
- package/Form/FormField.d.ts +6 -0
- package/Form/FormField.js +2 -2
- package/Form/FormHintText.d.ts +12 -0
- package/Form/FormHintText.js +3 -2
- package/Layout/Layout.d.ts +21 -5
- package/Layout/Layout.js +23 -19
- package/Layout/LayoutContext.d.ts +6 -6
- package/Layout/LayoutContext.js +2 -2
- package/Layout/LayoutHost.d.ts +0 -4
- package/Layout/LayoutHost.js +16 -13
- package/Layout/LayoutLeftPanel.d.ts +19 -0
- package/Layout/LayoutLeftPanel.js +86 -0
- package/Layout/LayoutMain.d.ts +10 -1
- package/Layout/LayoutMain.js +12 -3
- package/Layout/LayoutRightPanel.d.ts +19 -0
- package/Layout/{LayoutSidePanel.js → LayoutRightPanel.js} +32 -36
- package/Layout/index.d.ts +2 -2
- package/Layout/index.js +2 -1
- package/Modal/MediaPreviewModal.d.ts +4 -0
- package/Modal/MediaPreviewModal.js +2 -2
- package/Modal/Modal.d.ts +4 -0
- package/Modal/Modal.js +2 -2
- package/Modal/useModalContainer.js +0 -1
- package/Navigation/Navigation.d.ts +4 -0
- package/Navigation/Navigation.js +39 -3
- package/Navigation/NavigationFooter.js +19 -2
- package/Navigation/NavigationOption.d.ts +4 -0
- package/Navigation/NavigationOption.js +40 -19
- package/Navigation/NavigationOverflowMenuOption.js +11 -7
- package/Navigation/NavigationUserMenu.d.ts +1 -0
- package/Navigation/NavigationUserMenu.js +24 -5
- package/Navigation/context.d.ts +2 -0
- package/Navigation/context.js +4 -1
- package/Picker/RangePickerTrigger.js +1 -1
- package/Section/Section.js +6 -6
- package/Transition/Collapse.d.ts +2 -1
- package/Transition/Collapse.js +2 -1
- package/Upload/Upload.js +63 -9
- package/Upload/UploadPictureCard.d.ts +25 -15
- package/Upload/UploadPictureCard.js +14 -6
- package/index.d.ts +4 -2
- package/index.js +4 -1
- package/package.json +4 -4
- package/Layout/LayoutSidePanel.d.ts +0 -14
package/Description/index.d.ts
CHANGED
|
@@ -6,3 +6,5 @@ export { default as DescriptionContent } from './DescriptionContent';
|
|
|
6
6
|
export type { DescriptionContentProps } from './DescriptionContent';
|
|
7
7
|
export { default as DescriptionGroup } from './DescriptionGroup';
|
|
8
8
|
export type { DescriptionGroupProps } from './DescriptionGroup';
|
|
9
|
+
export { DescriptionContext } from './DescriptionContext';
|
|
10
|
+
export type { DescriptionContextValue } from './DescriptionContext';
|
package/Description/index.js
CHANGED
|
@@ -2,3 +2,4 @@ export { default as Description } from './Description.js';
|
|
|
2
2
|
export { default as DescriptionTitle } from './DescriptionTitle.js';
|
|
3
3
|
export { default as DescriptionContent } from './DescriptionContent.js';
|
|
4
4
|
export { default as DescriptionGroup } from './DescriptionGroup.js';
|
|
5
|
+
export { DescriptionContext } from './DescriptionContext.js';
|
package/Form/FormField.d.ts
CHANGED
|
@@ -37,6 +37,12 @@ export interface FormFieldProps extends NativeElementPropsWithoutKeyAndRef<'div'
|
|
|
37
37
|
* The icon to display alongside the hint text.
|
|
38
38
|
*/
|
|
39
39
|
hintTextIcon?: IconDefinition;
|
|
40
|
+
/**
|
|
41
|
+
* Whether to display the hint text icon.
|
|
42
|
+
* When false, neither the custom icon nor the default severity icon will be shown.
|
|
43
|
+
* @default true
|
|
44
|
+
*/
|
|
45
|
+
showHintTextIcon?: boolean;
|
|
40
46
|
/**
|
|
41
47
|
* The label text for the form field.
|
|
42
48
|
*/
|
package/Form/FormField.js
CHANGED
|
@@ -10,7 +10,7 @@ import cx from 'clsx';
|
|
|
10
10
|
* The React component for `mezzanine` form field.
|
|
11
11
|
*/
|
|
12
12
|
const FormField = forwardRef(function FormField(props, ref) {
|
|
13
|
-
const { children, className, counter, counterColor, controlFieldSlotLayout = ControlFieldSlotLayout.MAIN, density, disabled = false, fullWidth = false, hintText, hintTextIcon, label, labelInformationIcon, labelInformationText, labelOptionalMarker, labelSpacing = FormFieldLabelSpacing.MAIN, layout = FormFieldLayout.HORIZONTAL, name, required = false, severity = 'info', ...rest } = props;
|
|
13
|
+
const { children, className, counter, counterColor, controlFieldSlotLayout = ControlFieldSlotLayout.MAIN, density, disabled = false, fullWidth = false, hintText, hintTextIcon, showHintTextIcon, label, labelInformationIcon, labelInformationText, labelOptionalMarker, labelSpacing = FormFieldLabelSpacing.MAIN, layout = FormFieldLayout.HORIZONTAL, name, required = false, severity = 'info', ...rest } = props;
|
|
14
14
|
const formControl = {
|
|
15
15
|
disabled,
|
|
16
16
|
fullWidth,
|
|
@@ -30,7 +30,7 @@ const FormField = forwardRef(function FormField(props, ref) {
|
|
|
30
30
|
[formFieldClasses.fullWidth]: fullWidth,
|
|
31
31
|
}, className), children: jsxs(FormControlContext.Provider, { value: formControl, children: [label && (jsx(FormLabel, { className: cx(formFieldClasses.labelArea, labelSpacingClass), htmlFor: name, informationIcon: labelInformationIcon, informationText: labelInformationText, labelText: label, optionalMarker: labelOptionalMarker })), jsxs("div", { className: cx(formFieldClasses.dataEntry), children: [jsx("div", { className: cx(`${formFieldClasses.controlFieldSlot}--${controlFieldSlotLayout}`), children: children }), hintText || hintTextIcon || counter ? (jsxs("div", { className: cx(formFieldClasses.hintTextAndCounterArea, {
|
|
32
32
|
[formFieldClasses.hintTextAndCounterArea + '--align-right']: !(hintText || hintTextIcon) && counter,
|
|
33
|
-
}), 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] })] }) }));
|
|
33
|
+
}), children: [(hintText || hintTextIcon) && (jsx(FormHintText, { hintText: hintText, hintTextIcon: hintTextIcon, severity: severity, showHintTextIcon: showHintTextIcon })), counter && (jsx("span", { className: cx(formFieldClasses.counter, formFieldClasses.counterColor(counterColor || FormFieldCounterColor.INFO)), children: counter }))] })) : null] })] }) }));
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
export { FormField as default };
|
package/Form/FormHintText.d.ts
CHANGED
|
@@ -17,6 +17,12 @@ export type FormHintTextProps = NativeElementPropsWithoutKeyAndRef<'span'> & {
|
|
|
17
17
|
* if not provided, no icon will be displayed.
|
|
18
18
|
*/
|
|
19
19
|
severity?: keyof typeof formHintIcons | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Whether to display the hint text icon.
|
|
22
|
+
* When false, neither the custom icon nor the default severity icon will be shown.
|
|
23
|
+
* @default true
|
|
24
|
+
*/
|
|
25
|
+
showHintTextIcon?: boolean;
|
|
20
26
|
};
|
|
21
27
|
/**
|
|
22
28
|
* The React component for `mezzanine` form message.
|
|
@@ -37,5 +43,11 @@ declare const FormHintText: import("react").ForwardRefExoticComponent<NativeElem
|
|
|
37
43
|
* if not provided, no icon will be displayed.
|
|
38
44
|
*/
|
|
39
45
|
severity?: keyof typeof formHintIcons | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* Whether to display the hint text icon.
|
|
48
|
+
* When false, neither the custom icon nor the default severity icon will be shown.
|
|
49
|
+
* @default true
|
|
50
|
+
*/
|
|
51
|
+
showHintTextIcon?: boolean;
|
|
40
52
|
} & import("react").RefAttributes<HTMLSpanElement>>;
|
|
41
53
|
export default FormHintText;
|
package/Form/FormHintText.js
CHANGED
|
@@ -9,9 +9,10 @@ import cx from 'clsx';
|
|
|
9
9
|
* The React component for `mezzanine` form message.
|
|
10
10
|
*/
|
|
11
11
|
const FormHintText = forwardRef(function FormHintText(props, ref) {
|
|
12
|
-
const { className, hintText, hintTextIcon, severity = 'info', ...rest } = props;
|
|
12
|
+
const { className, hintText, hintTextIcon, severity = 'info', showHintTextIcon = true, ...rest } = props;
|
|
13
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: [
|
|
14
|
+
return (jsxs("span", { ...rest, ref: ref, className: cx(formFieldClasses.hintText, severity ? formFieldClasses.hintTextSeverity(severity) : undefined, className), children: [showHintTextIcon &&
|
|
15
|
+
(hintTextIcon ? (jsx(Icon, { className: formFieldClasses.hintTextIcon, icon: hintTextIcon, color: severity })) : (defaultIcon && (jsx(Icon, { className: formFieldClasses.hintTextIcon, icon: defaultIcon, color: severity })))), hintText] }));
|
|
15
16
|
});
|
|
16
17
|
|
|
17
18
|
export { FormHintText as default };
|
package/Layout/Layout.d.ts
CHANGED
|
@@ -1,16 +1,32 @@
|
|
|
1
1
|
import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
|
|
2
|
+
import { LayoutLeftPanel, LayoutLeftPanelProps } from './LayoutLeftPanel';
|
|
2
3
|
import { LayoutMain, LayoutMainProps } from './LayoutMain';
|
|
3
|
-
import {
|
|
4
|
+
import { LayoutRightPanel, LayoutRightPanelProps } from './LayoutRightPanel';
|
|
5
|
+
export { LayoutLeftPanel };
|
|
6
|
+
export type { LayoutLeftPanelProps };
|
|
4
7
|
export { LayoutMain };
|
|
5
8
|
export type { LayoutMainProps };
|
|
6
|
-
export {
|
|
7
|
-
export type {
|
|
9
|
+
export { LayoutRightPanel };
|
|
10
|
+
export type { LayoutRightPanelProps };
|
|
8
11
|
export interface LayoutProps extends NativeElementPropsWithoutKeyAndRef<'div'> {
|
|
9
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* Slot children. Place `<Navigation>`, `<Layout.LeftPanel>`, `<Layout.Main>`,
|
|
14
|
+
* and `<Layout.RightPanel>` as direct children in any order.
|
|
15
|
+
* They will be re-ordered into the correct DOM sequence automatically.
|
|
16
|
+
*/
|
|
10
17
|
children?: React.ReactNode;
|
|
18
|
+
/**
|
|
19
|
+
* Additional class name applied to the content wrapper element.
|
|
20
|
+
*/
|
|
21
|
+
contentWrapperClassName?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Additional class name applied to the navigation wrapper element. Only has effect if a `<Navigation>` component is provided as a child.
|
|
24
|
+
*/
|
|
25
|
+
navigationClassName?: string;
|
|
11
26
|
}
|
|
12
27
|
declare const LayoutWithSubComponents: import("react").ForwardRefExoticComponent<LayoutProps & import("react").RefAttributes<HTMLDivElement>> & {
|
|
28
|
+
LeftPanel: typeof LayoutLeftPanel;
|
|
13
29
|
Main: typeof LayoutMain;
|
|
14
|
-
|
|
30
|
+
RightPanel: typeof LayoutRightPanel;
|
|
15
31
|
};
|
|
16
32
|
export default LayoutWithSubComponents;
|
package/Layout/Layout.js
CHANGED
|
@@ -2,36 +2,40 @@ import { jsxs, jsx } from 'react/jsx-runtime';
|
|
|
2
2
|
import { forwardRef, Children, isValidElement } from 'react';
|
|
3
3
|
import { layoutClasses } from '@mezzanine-ui/core/layout';
|
|
4
4
|
import { LayoutHost } from './LayoutHost.js';
|
|
5
|
+
import { LayoutLeftPanel } from './LayoutLeftPanel.js';
|
|
5
6
|
import { LayoutMain } from './LayoutMain.js';
|
|
6
|
-
import {
|
|
7
|
+
import { LayoutRightPanel } from './LayoutRightPanel.js';
|
|
8
|
+
import Navigation from '../Navigation/Navigation.js';
|
|
9
|
+
import cx from 'clsx';
|
|
7
10
|
|
|
8
11
|
const Layout = forwardRef(function Layout(props, ref) {
|
|
9
|
-
const { children, ...rest } = props;
|
|
10
|
-
let
|
|
11
|
-
let
|
|
12
|
-
let
|
|
13
|
-
let
|
|
12
|
+
const { children, contentWrapperClassName, navigationClassName, ...rest } = props;
|
|
13
|
+
let navigationNode = null;
|
|
14
|
+
let leftPanelNode = null;
|
|
15
|
+
let mainNode = null;
|
|
16
|
+
let rightPanelNode = null;
|
|
14
17
|
Children.forEach(children, (child) => {
|
|
15
|
-
var _a, _b;
|
|
16
18
|
if (!isValidElement(child))
|
|
17
19
|
return;
|
|
18
|
-
if (child.type ===
|
|
19
|
-
|
|
20
|
-
.children;
|
|
20
|
+
if (child.type === Navigation) {
|
|
21
|
+
navigationNode = child;
|
|
21
22
|
}
|
|
22
|
-
else if (child.type ===
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
else if (child.type === LayoutLeftPanel) {
|
|
24
|
+
leftPanelNode = child;
|
|
25
|
+
}
|
|
26
|
+
else if (child.type === LayoutMain) {
|
|
27
|
+
mainNode = child;
|
|
28
|
+
}
|
|
29
|
+
else if (child.type === LayoutRightPanel) {
|
|
30
|
+
rightPanelNode = child;
|
|
28
31
|
}
|
|
29
32
|
});
|
|
30
|
-
return (jsxs(LayoutHost, { ...rest,
|
|
33
|
+
return (jsxs(LayoutHost, { ...rest, ref: ref, children: [navigationNode && (jsx("div", { className: cx(layoutClasses.navigation, navigationClassName), children: navigationNode })), jsxs("div", { className: cx(layoutClasses.contentWrapper, contentWrapperClassName), children: [leftPanelNode, mainNode, rightPanelNode] })] }));
|
|
31
34
|
});
|
|
32
35
|
const LayoutWithSubComponents = Object.assign(Layout, {
|
|
36
|
+
LeftPanel: LayoutLeftPanel,
|
|
33
37
|
Main: LayoutMain,
|
|
34
|
-
|
|
38
|
+
RightPanel: LayoutRightPanel,
|
|
35
39
|
});
|
|
36
40
|
|
|
37
|
-
export { LayoutMain,
|
|
41
|
+
export { LayoutLeftPanel, LayoutMain, LayoutRightPanel, LayoutWithSubComponents as default };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
export interface LayoutContextValue {
|
|
3
|
+
hostRef: RefObject<HTMLDivElement | null>;
|
|
4
|
+
mainRef: RefObject<HTMLDivElement | null>;
|
|
5
|
+
registerMain: (el: HTMLDivElement | null) => void;
|
|
6
6
|
}
|
|
7
|
-
export declare const
|
|
7
|
+
export declare const LayoutContext: import("react").Context<LayoutContextValue | null>;
|
package/Layout/LayoutContext.js
CHANGED
package/Layout/LayoutHost.d.ts
CHANGED
|
@@ -2,9 +2,5 @@ import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
|
|
|
2
2
|
export interface LayoutHostProps extends NativeElementPropsWithoutKeyAndRef<'div'> {
|
|
3
3
|
/** The content rendered inside the layout host. */
|
|
4
4
|
children?: React.ReactNode;
|
|
5
|
-
/** SSR hint: initial open state read from LayoutSidePanel.props.open */
|
|
6
|
-
initialOpen?: boolean;
|
|
7
|
-
/** SSR hint: initial width read from LayoutSidePanel.props.defaultSidePanelWidth */
|
|
8
|
-
initialSidePanelWidth?: number;
|
|
9
5
|
}
|
|
10
6
|
export declare const LayoutHost: import("react").ForwardRefExoticComponent<LayoutHostProps & import("react").RefAttributes<HTMLDivElement>>;
|
package/Layout/LayoutHost.js
CHANGED
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx } from 'react/jsx-runtime';
|
|
3
|
-
import { forwardRef,
|
|
3
|
+
import { forwardRef, useRef, useCallback, useMemo } from 'react';
|
|
4
4
|
import { layoutClasses } from '@mezzanine-ui/core/layout';
|
|
5
|
-
import {
|
|
5
|
+
import { LayoutContext } from './LayoutContext.js';
|
|
6
6
|
import cx from 'clsx';
|
|
7
7
|
|
|
8
|
-
const MIN_PANEL_WIDTH = 240;
|
|
9
8
|
const LayoutHost = forwardRef(function LayoutHost(props, ref) {
|
|
10
|
-
const { children, className,
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
setSidePanelWidth(width);
|
|
9
|
+
const { children, className, ...rest } = props;
|
|
10
|
+
const hostRef = useRef(null);
|
|
11
|
+
const mainRef = useRef(null);
|
|
12
|
+
const registerMain = useCallback((el) => {
|
|
13
|
+
mainRef.current = el;
|
|
16
14
|
}, []);
|
|
17
|
-
const contextValue = useMemo(() => ({
|
|
18
|
-
return (jsx(
|
|
19
|
-
|
|
20
|
-
'
|
|
15
|
+
const contextValue = useMemo(() => ({ hostRef, mainRef, registerMain }), [registerMain]);
|
|
16
|
+
return (jsx(LayoutContext.Provider, { value: contextValue, children: jsx("div", { ...rest, className: cx(layoutClasses.host, className), ref: (node) => {
|
|
17
|
+
hostRef.current = node;
|
|
18
|
+
if (typeof ref === 'function') {
|
|
19
|
+
ref(node);
|
|
20
|
+
}
|
|
21
|
+
else if (ref) {
|
|
22
|
+
ref.current = node;
|
|
23
|
+
}
|
|
21
24
|
}, children: children }) }));
|
|
22
25
|
});
|
|
23
26
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ScrollbarProps } from '../Scrollbar';
|
|
2
|
+
export interface LayoutLeftPanelProps {
|
|
3
|
+
/** The content rendered inside the left panel. */
|
|
4
|
+
children?: React.ReactNode;
|
|
5
|
+
/** Additional class name applied to the panel element. */
|
|
6
|
+
className?: string;
|
|
7
|
+
/** Initial width (in px) of the panel. Clamped to a minimum of 240px. */
|
|
8
|
+
defaultWidth?: number;
|
|
9
|
+
/** Callback fired when the panel width changes during resize. */
|
|
10
|
+
onWidthChange?: (width: number) => void;
|
|
11
|
+
/** Controls whether the panel and its divider are visible. */
|
|
12
|
+
open?: boolean;
|
|
13
|
+
/** Props passed to the internal `Scrollbar` component. */
|
|
14
|
+
scrollbarProps?: Omit<ScrollbarProps, 'children'>;
|
|
15
|
+
}
|
|
16
|
+
export declare function LayoutLeftPanel({ children, className, defaultWidth, onWidthChange, open, scrollbarProps, }: LayoutLeftPanelProps): import("react/jsx-runtime").JSX.Element | null;
|
|
17
|
+
export declare namespace LayoutLeftPanel {
|
|
18
|
+
var displayName: string;
|
|
19
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
+
import { useContext, useState, useRef, useEffect, useCallback } from 'react';
|
|
4
|
+
import { layoutClasses } from '@mezzanine-ui/core/layout';
|
|
5
|
+
import { useDocumentEvents } from '../hooks/useDocumentEvents.js';
|
|
6
|
+
import { LayoutContext } from './LayoutContext.js';
|
|
7
|
+
import Scrollbar from '../Scrollbar/Scrollbar.js';
|
|
8
|
+
import cx from 'clsx';
|
|
9
|
+
|
|
10
|
+
const MIN_PANEL_WIDTH = 240;
|
|
11
|
+
const CONTENT_WRAPPER_MIN_WIDTH = 480;
|
|
12
|
+
const ARROW_KEY_STEP = 10;
|
|
13
|
+
function LayoutLeftPanel({ children, className, defaultWidth = 320, onWidthChange, open = false, scrollbarProps = {}, }) {
|
|
14
|
+
const context = useContext(LayoutContext);
|
|
15
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
16
|
+
const [width, setWidth] = useState(() => Math.max(MIN_PANEL_WIDTH, defaultWidth));
|
|
17
|
+
const dragStartRef = useRef(null);
|
|
18
|
+
const rafIdRef = useRef(null);
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
return () => {
|
|
21
|
+
if (rafIdRef.current !== null) {
|
|
22
|
+
window.cancelAnimationFrame(rafIdRef.current);
|
|
23
|
+
rafIdRef.current = null;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}, []);
|
|
27
|
+
const handleDividerMouseDown = useCallback((e) => {
|
|
28
|
+
var _a;
|
|
29
|
+
e.preventDefault();
|
|
30
|
+
const mainWidth = ((_a = context === null || context === void 0 ? void 0 : context.mainRef.current) === null || _a === void 0 ? void 0 : _a.offsetWidth) || window.innerWidth;
|
|
31
|
+
const maxWidth = width + Math.max(0, mainWidth - CONTENT_WRAPPER_MIN_WIDTH);
|
|
32
|
+
setIsDragging(true);
|
|
33
|
+
dragStartRef.current = { maxWidth, width, x: e.clientX };
|
|
34
|
+
}, [context, width]);
|
|
35
|
+
const handleDividerKeyDown = useCallback((e) => {
|
|
36
|
+
var _a;
|
|
37
|
+
if (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight')
|
|
38
|
+
return;
|
|
39
|
+
e.preventDefault();
|
|
40
|
+
const mainWidth = ((_a = context === null || context === void 0 ? void 0 : context.mainRef.current) === null || _a === void 0 ? void 0 : _a.offsetWidth) || window.innerWidth;
|
|
41
|
+
const maxWidth = width + Math.max(0, mainWidth - CONTENT_WRAPPER_MIN_WIDTH);
|
|
42
|
+
const step = e.key === 'ArrowRight' ? ARROW_KEY_STEP : -ARROW_KEY_STEP;
|
|
43
|
+
const newWidth = Math.min(maxWidth, Math.max(MIN_PANEL_WIDTH, width + step));
|
|
44
|
+
setWidth(newWidth);
|
|
45
|
+
onWidthChange === null || onWidthChange === void 0 ? void 0 : onWidthChange(newWidth);
|
|
46
|
+
}, [context, onWidthChange, width]);
|
|
47
|
+
useDocumentEvents(() => {
|
|
48
|
+
if (!isDragging)
|
|
49
|
+
return undefined;
|
|
50
|
+
return {
|
|
51
|
+
mousemove: (e) => {
|
|
52
|
+
if (!dragStartRef.current)
|
|
53
|
+
return;
|
|
54
|
+
if (rafIdRef.current !== null) {
|
|
55
|
+
window.cancelAnimationFrame(rafIdRef.current);
|
|
56
|
+
}
|
|
57
|
+
rafIdRef.current = window.requestAnimationFrame(() => {
|
|
58
|
+
rafIdRef.current = null;
|
|
59
|
+
if (!dragStartRef.current)
|
|
60
|
+
return;
|
|
61
|
+
const delta = e.clientX - dragStartRef.current.x;
|
|
62
|
+
const newWidth = dragStartRef.current.width + delta;
|
|
63
|
+
const clamped = Math.min(dragStartRef.current.maxWidth, Math.max(MIN_PANEL_WIDTH, newWidth));
|
|
64
|
+
setWidth(clamped);
|
|
65
|
+
onWidthChange === null || onWidthChange === void 0 ? void 0 : onWidthChange(clamped);
|
|
66
|
+
});
|
|
67
|
+
},
|
|
68
|
+
mouseup: () => {
|
|
69
|
+
if (rafIdRef.current !== null) {
|
|
70
|
+
window.cancelAnimationFrame(rafIdRef.current);
|
|
71
|
+
rafIdRef.current = null;
|
|
72
|
+
}
|
|
73
|
+
setIsDragging(false);
|
|
74
|
+
dragStartRef.current = null;
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}, [isDragging, onWidthChange]);
|
|
78
|
+
if (!open)
|
|
79
|
+
return null;
|
|
80
|
+
return (jsxs("aside", { "aria-label": "Left panel", className: cx(layoutClasses.sidePanel, layoutClasses.sidePanelLeft, className), style: { inlineSize: width }, children: [jsx("div", { className: layoutClasses.sidePanelContent, children: jsx(Scrollbar, { ...scrollbarProps, children: children }) }), jsx("div", { "aria-label": "Resize left panel", "aria-orientation": "vertical", "aria-valuemin": MIN_PANEL_WIDTH, "aria-valuenow": width, className: cx(layoutClasses.divider, {
|
|
81
|
+
[layoutClasses.dividerDragging]: isDragging,
|
|
82
|
+
}), onKeyDown: handleDividerKeyDown, onMouseDown: handleDividerMouseDown, role: "separator", tabIndex: 0 })] }));
|
|
83
|
+
}
|
|
84
|
+
LayoutLeftPanel.displayName = 'Layout.LeftPanel';
|
|
85
|
+
|
|
86
|
+
export { LayoutLeftPanel };
|
package/Layout/LayoutMain.d.ts
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
|
+
import { ScrollbarProps } from '../Scrollbar';
|
|
1
2
|
export interface LayoutMainProps {
|
|
2
3
|
/** The content rendered inside the main area. */
|
|
3
4
|
children?: React.ReactNode;
|
|
5
|
+
/**
|
|
6
|
+
* Additional class name applied to the main element.
|
|
7
|
+
*/
|
|
8
|
+
className?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Props passed to the internal `Scrollbar` component. If not provided, the main area will still be scrollable but without the custom scrollbar styling and behavior.
|
|
11
|
+
*/
|
|
12
|
+
scrollbarProps?: Omit<ScrollbarProps, 'children'>;
|
|
4
13
|
}
|
|
5
|
-
export declare function LayoutMain(
|
|
14
|
+
export declare function LayoutMain(props: LayoutMainProps): import("react/jsx-runtime").JSX.Element;
|
|
6
15
|
export declare namespace LayoutMain {
|
|
7
16
|
var displayName: string;
|
|
8
17
|
}
|
package/Layout/LayoutMain.js
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx } from 'react/jsx-runtime';
|
|
3
|
+
import { useContext } from 'react';
|
|
4
|
+
import { layoutClasses } from '@mezzanine-ui/core/layout';
|
|
5
|
+
import { LayoutContext } from './LayoutContext.js';
|
|
6
|
+
import Scrollbar from '../Scrollbar/Scrollbar.js';
|
|
7
|
+
import cx from 'clsx';
|
|
2
8
|
|
|
3
|
-
function LayoutMain(
|
|
4
|
-
|
|
9
|
+
function LayoutMain(props) {
|
|
10
|
+
var _a;
|
|
11
|
+
const { children, className, scrollbarProps = {} } = props;
|
|
12
|
+
const context = useContext(LayoutContext);
|
|
13
|
+
return (jsx("div", { ref: (_a = context === null || context === void 0 ? void 0 : context.registerMain) !== null && _a !== void 0 ? _a : null, className: cx(layoutClasses.main, className), children: jsx(Scrollbar, { ...scrollbarProps, children: jsx("div", { className: layoutClasses.mainContent, children: children }) }) }));
|
|
5
14
|
}
|
|
6
15
|
LayoutMain.displayName = 'Layout.Main';
|
|
7
16
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ScrollbarProps } from '../Scrollbar';
|
|
2
|
+
export interface LayoutRightPanelProps {
|
|
3
|
+
/** The content rendered inside the right panel. */
|
|
4
|
+
children?: React.ReactNode;
|
|
5
|
+
/** Additional class name applied to the panel element. */
|
|
6
|
+
className?: string;
|
|
7
|
+
/** Initial width (in px) of the panel. Clamped to a minimum of 240px. */
|
|
8
|
+
defaultWidth?: number;
|
|
9
|
+
/** Callback fired when the panel width changes during resize. */
|
|
10
|
+
onWidthChange?: (width: number) => void;
|
|
11
|
+
/** Controls whether the panel and its divider are visible. */
|
|
12
|
+
open?: boolean;
|
|
13
|
+
/** Props passed to the internal `Scrollbar` component. */
|
|
14
|
+
scrollbarProps?: Omit<ScrollbarProps, 'children'>;
|
|
15
|
+
}
|
|
16
|
+
export declare function LayoutRightPanel({ children, className, defaultWidth, onWidthChange, open, scrollbarProps, }: LayoutRightPanelProps): import("react/jsx-runtime").JSX.Element | null;
|
|
17
|
+
export declare namespace LayoutRightPanel {
|
|
18
|
+
var displayName: string;
|
|
19
|
+
}
|
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsxs,
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
3
|
import { useContext, useState, useRef, useEffect, useCallback } from 'react';
|
|
4
4
|
import { layoutClasses } from '@mezzanine-ui/core/layout';
|
|
5
5
|
import { useDocumentEvents } from '../hooks/useDocumentEvents.js';
|
|
6
|
-
import {
|
|
6
|
+
import { LayoutContext } from './LayoutContext.js';
|
|
7
|
+
import Scrollbar from '../Scrollbar/Scrollbar.js';
|
|
7
8
|
import cx from 'clsx';
|
|
8
9
|
|
|
9
10
|
const MIN_PANEL_WIDTH = 240;
|
|
11
|
+
const CONTENT_WRAPPER_MIN_WIDTH = 480;
|
|
10
12
|
const ARROW_KEY_STEP = 10;
|
|
11
|
-
function
|
|
12
|
-
const context = useContext(
|
|
13
|
+
function LayoutRightPanel({ children, className, defaultWidth = 320, onWidthChange, open = false, scrollbarProps = {}, }) {
|
|
14
|
+
const context = useContext(LayoutContext);
|
|
13
15
|
const [isDragging, setIsDragging] = useState(false);
|
|
14
|
-
const [
|
|
15
|
-
const maxWidth = typeof window !== 'undefined'
|
|
16
|
-
? window.innerWidth - MIN_PANEL_WIDTH - 1
|
|
17
|
-
: Infinity;
|
|
18
|
-
return Math.min(maxWidth, Math.max(MIN_PANEL_WIDTH, defaultSidePanelWidth));
|
|
19
|
-
});
|
|
16
|
+
const [width, setWidth] = useState(() => Math.max(MIN_PANEL_WIDTH, defaultWidth));
|
|
20
17
|
const dragStartRef = useRef(null);
|
|
21
18
|
const rafIdRef = useRef(null);
|
|
22
19
|
useEffect(() => {
|
|
@@ -27,24 +24,26 @@ function LayoutSidePanel({ children, defaultSidePanelWidth = 320, onSidePanelWid
|
|
|
27
24
|
}
|
|
28
25
|
};
|
|
29
26
|
}, []);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
const handleDividerMouseDown = useCallback((e) => {
|
|
28
|
+
var _a;
|
|
29
|
+
e.preventDefault();
|
|
30
|
+
const mainWidth = ((_a = context === null || context === void 0 ? void 0 : context.mainRef.current) === null || _a === void 0 ? void 0 : _a.offsetWidth) || window.innerWidth;
|
|
31
|
+
const maxWidth = width + Math.max(0, mainWidth - CONTENT_WRAPPER_MIN_WIDTH);
|
|
32
|
+
setIsDragging(true);
|
|
33
|
+
dragStartRef.current = { maxWidth, width, x: e.clientX };
|
|
34
|
+
}, [context, width]);
|
|
33
35
|
const handleDividerKeyDown = useCallback((e) => {
|
|
36
|
+
var _a;
|
|
34
37
|
if (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight')
|
|
35
38
|
return;
|
|
36
39
|
e.preventDefault();
|
|
40
|
+
const mainWidth = ((_a = context === null || context === void 0 ? void 0 : context.mainRef.current) === null || _a === void 0 ? void 0 : _a.offsetWidth) || window.innerWidth;
|
|
41
|
+
const maxWidth = width + Math.max(0, mainWidth - CONTENT_WRAPPER_MIN_WIDTH);
|
|
37
42
|
const step = e.key === 'ArrowLeft' ? ARROW_KEY_STEP : -ARROW_KEY_STEP;
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}, [onSidePanelWidthChange, sidePanelWidth]);
|
|
43
|
-
const handleDividerMouseDown = useCallback((e) => {
|
|
44
|
-
e.preventDefault();
|
|
45
|
-
setIsDragging(true);
|
|
46
|
-
dragStartRef.current = { width: sidePanelWidth, x: e.clientX };
|
|
47
|
-
}, [sidePanelWidth]);
|
|
43
|
+
const newWidth = Math.min(maxWidth, Math.max(MIN_PANEL_WIDTH, width + step));
|
|
44
|
+
setWidth(newWidth);
|
|
45
|
+
onWidthChange === null || onWidthChange === void 0 ? void 0 : onWidthChange(newWidth);
|
|
46
|
+
}, [context, onWidthChange, width]);
|
|
48
47
|
useDocumentEvents(() => {
|
|
49
48
|
if (!isDragging)
|
|
50
49
|
return undefined;
|
|
@@ -59,12 +58,11 @@ function LayoutSidePanel({ children, defaultSidePanelWidth = 320, onSidePanelWid
|
|
|
59
58
|
rafIdRef.current = null;
|
|
60
59
|
if (!dragStartRef.current)
|
|
61
60
|
return;
|
|
62
|
-
const delta = dragStartRef.current.x
|
|
63
|
-
const newWidth = dragStartRef.current.width
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
onSidePanelWidthChange === null || onSidePanelWidthChange === void 0 ? void 0 : onSidePanelWidthChange(clamped);
|
|
61
|
+
const delta = e.clientX - dragStartRef.current.x;
|
|
62
|
+
const newWidth = dragStartRef.current.width - delta;
|
|
63
|
+
const clamped = Math.min(dragStartRef.current.maxWidth, Math.max(MIN_PANEL_WIDTH, newWidth));
|
|
64
|
+
setWidth(clamped);
|
|
65
|
+
onWidthChange === null || onWidthChange === void 0 ? void 0 : onWidthChange(clamped);
|
|
68
66
|
});
|
|
69
67
|
},
|
|
70
68
|
mouseup: () => {
|
|
@@ -76,15 +74,13 @@ function LayoutSidePanel({ children, defaultSidePanelWidth = 320, onSidePanelWid
|
|
|
76
74
|
dragStartRef.current = null;
|
|
77
75
|
},
|
|
78
76
|
};
|
|
79
|
-
}, [isDragging,
|
|
77
|
+
}, [isDragging, onWidthChange]);
|
|
80
78
|
if (!open)
|
|
81
79
|
return null;
|
|
82
|
-
return (jsxs(
|
|
83
|
-
? window.innerWidth - MIN_PANEL_WIDTH - 1
|
|
84
|
-
: undefined, "aria-valuemin": MIN_PANEL_WIDTH, "aria-valuenow": sidePanelWidth, className: cx(layoutClasses.divider, {
|
|
80
|
+
return (jsxs("aside", { "aria-label": "Right panel", className: cx(layoutClasses.sidePanel, layoutClasses.sidePanelRight, className), style: { inlineSize: width }, children: [jsx("div", { "aria-label": "Resize right panel", "aria-orientation": "vertical", "aria-valuemin": MIN_PANEL_WIDTH, "aria-valuenow": width, className: cx(layoutClasses.divider, {
|
|
85
81
|
[layoutClasses.dividerDragging]: isDragging,
|
|
86
|
-
}), onKeyDown: handleDividerKeyDown, onMouseDown: handleDividerMouseDown, role: "separator", tabIndex: 0 }), jsx("
|
|
82
|
+
}), onKeyDown: handleDividerKeyDown, onMouseDown: handleDividerMouseDown, role: "separator", tabIndex: 0 }), jsx("div", { className: layoutClasses.sidePanelContent, children: jsx(Scrollbar, { ...scrollbarProps, children: children }) })] }));
|
|
87
83
|
}
|
|
88
|
-
|
|
84
|
+
LayoutRightPanel.displayName = 'Layout.RightPanel';
|
|
89
85
|
|
|
90
|
-
export {
|
|
86
|
+
export { LayoutRightPanel };
|
package/Layout/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { default, LayoutMain,
|
|
2
|
-
export type { LayoutMainProps, LayoutProps,
|
|
1
|
+
export { default, LayoutLeftPanel, LayoutMain, LayoutRightPanel, } from './Layout';
|
|
2
|
+
export type { LayoutLeftPanelProps, LayoutMainProps, LayoutProps, LayoutRightPanelProps, } from './Layout';
|
|
3
3
|
export type { LayoutHostProps } from './LayoutHost';
|
package/Layout/index.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
|
|
2
2
|
import { ModalContainerProps } from './useModalContainer';
|
|
3
3
|
export interface MediaPreviewModalProps extends Omit<ModalContainerProps, 'children'>, NativeElementPropsWithoutKeyAndRef<'div'> {
|
|
4
|
+
/**
|
|
5
|
+
* The custom class name applied to the modal container.
|
|
6
|
+
*/
|
|
7
|
+
backdropClassName?: string;
|
|
4
8
|
/**
|
|
5
9
|
* The current index of the media being displayed (controlled mode).
|
|
6
10
|
* If provided along with onNext/onPrev, the component operates in controlled mode.
|
|
@@ -15,7 +15,7 @@ import cx from 'clsx';
|
|
|
15
15
|
* Displays media items with navigation controls and a close button.
|
|
16
16
|
*/
|
|
17
17
|
const MediaPreviewModal = forwardRef(function MediaPreviewModal(props, ref) {
|
|
18
|
-
const { className, container, currentIndex: controlledIndex, defaultIndex = 0, disableCloseOnBackdropClick = false, disableCloseOnEscapeKeyDown = false, disableNext = false, disablePortal = false, disablePrev = false, enableCircularNavigation = false, mediaItems, onBackdropClick, onClose, onIndexChange, onNext, onPrev, open, showPaginationIndicator = true, ...rest } = props;
|
|
18
|
+
const { backdropClassName, className, container, currentIndex: controlledIndex, defaultIndex = 0, disableCloseOnBackdropClick = false, disableCloseOnEscapeKeyDown = false, disableNext = false, disablePortal = false, disablePrev = false, enableCircularNavigation = false, mediaItems, onBackdropClick, onClose, onIndexChange, onNext, onPrev, open, showPaginationIndicator = true, ...rest } = props;
|
|
19
19
|
const { Container: ModalContainer } = useModalContainer();
|
|
20
20
|
// Determine if component is in controlled mode
|
|
21
21
|
const isControlled = controlledIndex !== undefined;
|
|
@@ -169,7 +169,7 @@ const MediaPreviewModal = forwardRef(function MediaPreviewModal(props, ref) {
|
|
|
169
169
|
exit: MOTION_EASING.standard,
|
|
170
170
|
}, in: isCurrent, children: mediaElement.element }, index));
|
|
171
171
|
};
|
|
172
|
-
return (jsxs(ModalContainer, { className:
|
|
172
|
+
return (jsxs(ModalContainer, { className: backdropClassName, container: container, disableCloseOnBackdropClick: disableCloseOnBackdropClick, disableCloseOnEscapeKeyDown: disableCloseOnEscapeKeyDown, disablePortal: disablePortal, onBackdropClick: onBackdropClick, onClose: onClose, open: open, ref: ref, children: [jsx("div", { ...rest, className: cx(modalClasses.host, modalClasses.mediaPreview, className), role: "dialog", children: jsx("div", { className: modalClasses.mediaPreviewContent, children: jsx("div", { className: modalClasses.mediaPreviewMediaContainer, children: displayedIndices.map((index) => renderMedia(index)) }) }) }), jsx(ClearActions, { className: modalClasses.mediaPreviewCloseButton, onClick: onClose, type: "embedded", variant: "contrast" }), mediaItems.length > 1 && (jsx("button", { "aria-disabled": isPrevDisabled, "aria-label": "Previous media", className: cx(modalClasses.mediaPreviewNavButton, modalClasses.mediaPreviewNavButtonPrev), disabled: isPrevDisabled, onClick: handlePrev, title: "Previous", type: "button", children: jsx(Icon, { icon: ChevronLeftIcon, size: 16, color: "fixed-light" }) })), mediaItems.length > 1 && (jsx("button", { "aria-disabled": isNextDisabled, "aria-label": "Next media", className: cx(modalClasses.mediaPreviewNavButton, modalClasses.mediaPreviewNavButtonNext), disabled: isNextDisabled, onClick: handleNext, title: "Next", type: "button", children: jsx(Icon, { icon: ChevronRightIcon, size: 16, color: "fixed-light" }) })), showPaginationIndicator && mediaItems.length > 1 && (jsxs("div", { "aria-label": `Page ${currentIndex + 1} of ${mediaItems.length}`, className: modalClasses.mediaPreviewPaginationIndicator, children: [currentIndex + 1, "/", mediaItems.length] }))] }));
|
|
173
173
|
});
|
|
174
174
|
|
|
175
175
|
export { MediaPreviewModal as default };
|
package/Modal/Modal.d.ts
CHANGED
|
@@ -4,6 +4,10 @@ import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
|
|
|
4
4
|
import { ModalHeaderProps } from './ModalHeader';
|
|
5
5
|
import { ModalFooterProps } from './ModalFooter';
|
|
6
6
|
interface CommonModalProps extends Omit<ModalContainerProps, 'children'>, Pick<NativeElementPropsWithoutKeyAndRef<'div'>, 'children'>, Partial<Omit<ModalHeaderProps, 'children' | 'className' | 'title'>>, Partial<Omit<ModalFooterProps, 'children' | 'className' | 'confirmText'>> {
|
|
7
|
+
/**
|
|
8
|
+
* The custom class name applied to the modal container.
|
|
9
|
+
*/
|
|
10
|
+
backdropClassName?: string;
|
|
7
11
|
/**
|
|
8
12
|
* Whether to force full screen on any breakpoint.
|
|
9
13
|
* @default false
|