@astral/ui 4.51.0 → 4.52.1
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/components/DashboardContext/DashboardContext.d.ts +2 -0
- package/components/DashboardContext/DashboardContext.js +2 -0
- package/components/DashboardContext/DashboardContextProvider/DashboardContextProvider.d.ts +2 -2
- package/components/DashboardContext/DashboardContextProvider/DashboardContextProvider.js +4 -1
- package/components/DashboardLayout/DashboardLayout.d.ts +1 -1
- package/components/DashboardLayout/DashboardLayout.js +2 -2
- package/components/DashboardLayout/Header/Header.js +28 -3
- package/components/DashboardLayout/Header/constants.d.ts +5 -1
- package/components/DashboardLayout/Header/constants.js +5 -1
- package/components/DashboardLayout/Header/styles.d.ts +4 -3
- package/components/DashboardLayout/Header/styles.js +66 -24
- package/components/DashboardLayout/Header/useLogic/useLogic.d.ts +4 -1
- package/components/DashboardLayout/Header/useLogic/useLogic.js +16 -11
- package/components/DashboardLayout/types.d.ts +5 -0
- package/components/DashboardSidebar/DashboardSidebar.js +17 -2
- package/components/DashboardSidebar/constants.d.ts +1 -0
- package/components/DashboardSidebar/constants.js +1 -0
- package/components/DashboardSidebar/styles.js +1 -1
- package/components/DashboardSidebar/useLogic/useLogic.d.ts +2 -1
- package/components/DashboardSidebar/useLogic/useLogic.js +5 -6
- package/components/DatePicker/hooks/useDatePickerOptions/useDatePickerOptions.d.ts +2 -0
- package/components/DatePicker/hooks/useDatePickerOptions/useDatePickerOptions.js +2 -1
- package/components/DatePicker/hooks/useMaskedValue/useMaskedValue.d.ts +4 -0
- package/components/DatePicker/hooks/useMaskedValue/useMaskedValue.js +37 -8
- package/components/DatePicker/useLogic/useLogic.d.ts +1 -0
- package/components/DatePicker/useLogic/useLogic.js +19 -13
- package/components/DateRangePicker/useLogic/useLogic.d.ts +2 -0
- package/components/DateRangePicker/useLogic/useLogic.js +27 -3
- package/components/DateRangePicker/useLogic/utils/index.d.ts +0 -1
- package/components/DateRangePicker/useLogic/utils/index.js +0 -1
- package/components/MenuOrganization/OrganizationButton/OrganizationButton.js +1 -1
- package/components/MenuOrganization/constants.d.ts +1 -0
- package/components/MenuOrganization/constants.js +1 -0
- package/components/Profile/Profile.js +1 -1
- package/components/Profile/constants.d.ts +3 -0
- package/components/Profile/constants.js +3 -0
- package/components/utils/date/format/index.d.ts +1 -0
- package/components/utils/date/format/index.js +1 -0
- package/{node/components/DateRangePicker/useLogic/utils → components/utils/date/format}/isMaskedDateSyntacticallyComplete/isMaskedDateSyntacticallyComplete.d.ts +1 -1
- package/components/utils/date/format/parseDate/parseDate.d.ts +1 -0
- package/components/utils/date/format/parseDate/parseDate.js +42 -13
- package/node/components/DashboardContext/DashboardContext.d.ts +2 -0
- package/node/components/DashboardContext/DashboardContext.js +2 -0
- package/node/components/DashboardContext/DashboardContextProvider/DashboardContextProvider.d.ts +2 -2
- package/node/components/DashboardContext/DashboardContextProvider/DashboardContextProvider.js +4 -1
- package/node/components/DashboardLayout/DashboardLayout.d.ts +1 -1
- package/node/components/DashboardLayout/DashboardLayout.js +2 -2
- package/node/components/DashboardLayout/Header/Header.js +27 -2
- package/node/components/DashboardLayout/Header/constants.d.ts +5 -1
- package/node/components/DashboardLayout/Header/constants.js +5 -1
- package/node/components/DashboardLayout/Header/styles.d.ts +4 -3
- package/node/components/DashboardLayout/Header/styles.js +74 -32
- package/node/components/DashboardLayout/Header/useLogic/useLogic.d.ts +4 -1
- package/node/components/DashboardLayout/Header/useLogic/useLogic.js +15 -10
- package/node/components/DashboardLayout/types.d.ts +5 -0
- package/node/components/DashboardSidebar/DashboardSidebar.js +17 -2
- package/node/components/DashboardSidebar/constants.d.ts +1 -0
- package/node/components/DashboardSidebar/constants.js +1 -0
- package/node/components/DashboardSidebar/styles.js +1 -1
- package/node/components/DashboardSidebar/useLogic/useLogic.d.ts +2 -1
- package/node/components/DashboardSidebar/useLogic/useLogic.js +4 -5
- package/node/components/DatePicker/hooks/useDatePickerOptions/useDatePickerOptions.d.ts +2 -0
- package/node/components/DatePicker/hooks/useDatePickerOptions/useDatePickerOptions.js +2 -1
- package/node/components/DatePicker/hooks/useMaskedValue/useMaskedValue.d.ts +4 -0
- package/node/components/DatePicker/hooks/useMaskedValue/useMaskedValue.js +35 -6
- package/node/components/DatePicker/useLogic/useLogic.d.ts +1 -0
- package/node/components/DatePicker/useLogic/useLogic.js +19 -13
- package/node/components/DateRangePicker/useLogic/useLogic.d.ts +2 -0
- package/node/components/DateRangePicker/useLogic/useLogic.js +28 -4
- package/node/components/DateRangePicker/useLogic/utils/index.d.ts +0 -1
- package/node/components/DateRangePicker/useLogic/utils/index.js +0 -1
- package/node/components/MenuOrganization/OrganizationButton/OrganizationButton.js +1 -1
- package/node/components/MenuOrganization/constants.d.ts +1 -0
- package/node/components/MenuOrganization/constants.js +1 -0
- package/node/components/Profile/Profile.js +1 -1
- package/node/components/Profile/constants.d.ts +3 -0
- package/node/components/Profile/constants.js +3 -0
- package/node/components/utils/date/format/index.d.ts +1 -0
- package/node/components/utils/date/format/index.js +1 -0
- package/{components/DateRangePicker/useLogic/utils → node/components/utils/date/format}/isMaskedDateSyntacticallyComplete/isMaskedDateSyntacticallyComplete.d.ts +1 -1
- package/node/components/utils/date/format/parseDate/parseDate.d.ts +1 -0
- package/node/components/utils/date/format/parseDate/parseDate.js +45 -13
- package/package.json +1 -1
- /package/components/{DateRangePicker/useLogic/utils → utils/date/format}/isMaskedDateSyntacticallyComplete/index.d.ts +0 -0
- /package/components/{DateRangePicker/useLogic/utils → utils/date/format}/isMaskedDateSyntacticallyComplete/index.js +0 -0
- /package/components/{DateRangePicker/useLogic/utils → utils/date/format}/isMaskedDateSyntacticallyComplete/isMaskedDateSyntacticallyComplete.js +0 -0
- /package/node/components/{DateRangePicker/useLogic/utils → utils/date/format}/isMaskedDateSyntacticallyComplete/index.d.ts +0 -0
- /package/node/components/{DateRangePicker/useLogic/utils → utils/date/format}/isMaskedDateSyntacticallyComplete/index.js +0 -0
- /package/node/components/{DateRangePicker/useLogic/utils → utils/date/format}/isMaskedDateSyntacticallyComplete/isMaskedDateSyntacticallyComplete.js +0 -0
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { type RefObject } from 'react';
|
|
2
2
|
export type DashboardContextProps = {
|
|
3
3
|
isFocusedMode: boolean;
|
|
4
|
+
mobileHeaderPriorityFeature: 'profile' | 'menuOrg';
|
|
4
5
|
setAlertElement?: (element: HTMLElement | null) => void;
|
|
5
6
|
alertHeight: number;
|
|
6
7
|
isLoading: boolean;
|
|
7
8
|
hasMenuOrganizationRef: RefObject<boolean | null>;
|
|
9
|
+
hasProfileRef: RefObject<boolean | null>;
|
|
8
10
|
setFocusedMode: (isFocusedMode: boolean) => void;
|
|
9
11
|
};
|
|
10
12
|
export declare const DashboardContext: import("react").Context<DashboardContextProps>;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { createContext, createRef } from 'react';
|
|
2
2
|
export const DashboardContext = createContext({
|
|
3
3
|
isFocusedMode: false,
|
|
4
|
+
mobileHeaderPriorityFeature: 'menuOrg',
|
|
4
5
|
alertHeight: 0,
|
|
5
6
|
isLoading: false,
|
|
6
7
|
hasMenuOrganizationRef: createRef(),
|
|
8
|
+
hasProfileRef: createRef(),
|
|
7
9
|
setFocusedMode: () => undefined,
|
|
8
10
|
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import { type DashboardContextProps } from '../DashboardContext';
|
|
3
|
-
type Props = Pick<DashboardContextProps, 'isFocusedMode'> & {
|
|
3
|
+
type Props = Pick<DashboardContextProps, 'isFocusedMode' | 'mobileHeaderPriorityFeature'> & {
|
|
4
4
|
children: ReactNode;
|
|
5
5
|
isLoading: boolean;
|
|
6
6
|
};
|
|
7
|
-
export declare const DashboardContextProvider: ({ children, isFocusedMode, isLoading, }: Props) => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export declare const DashboardContextProvider: ({ children, isFocusedMode, isLoading, mobileHeaderPriorityFeature, }: Props) => import("react/jsx-runtime").JSX.Element;
|
|
8
8
|
export {};
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef, useState } from 'react';
|
|
3
3
|
import { DashboardContext, } from '../DashboardContext';
|
|
4
|
-
export const DashboardContextProvider = ({ children, isFocusedMode, isLoading, }) => {
|
|
4
|
+
export const DashboardContextProvider = ({ children, isFocusedMode, isLoading, mobileHeaderPriorityFeature, }) => {
|
|
5
5
|
const [alertElement, setAlertElement] = useState(null);
|
|
6
6
|
const [alertHeight, setAlertHeight] = useState(0);
|
|
7
7
|
const [isFocusedModeInternal, setFocusedMode] = useState(isFocusedMode);
|
|
8
8
|
const hasMenuOrganizationRef = useRef(false);
|
|
9
|
+
const hasProfileRef = useRef(false);
|
|
9
10
|
useEffect(() => {
|
|
10
11
|
if (isFocusedMode !== isFocusedModeInternal) {
|
|
11
12
|
setFocusedMode(isFocusedMode);
|
|
@@ -27,9 +28,11 @@ export const DashboardContextProvider = ({ children, isFocusedMode, isLoading, }
|
|
|
27
28
|
}, [alertElement]);
|
|
28
29
|
return (_jsx(DashboardContext.Provider, { value: {
|
|
29
30
|
isFocusedMode: isFocusedModeInternal,
|
|
31
|
+
mobileHeaderPriorityFeature,
|
|
30
32
|
setAlertElement,
|
|
31
33
|
alertHeight,
|
|
32
34
|
isLoading,
|
|
35
|
+
hasProfileRef,
|
|
33
36
|
hasMenuOrganizationRef,
|
|
34
37
|
setFocusedMode,
|
|
35
38
|
}, children: children }));
|
|
@@ -4,7 +4,7 @@ import { type DashboardLayoutProps } from './types';
|
|
|
4
4
|
* Общий Layout приложения
|
|
5
5
|
*/
|
|
6
6
|
export declare const DashboardLayout: {
|
|
7
|
-
({ children, isFocusedMode, isLoading, }: DashboardLayoutProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
({ children, isFocusedMode, isLoading, mobileHeaderPriorityFeature, }: DashboardLayoutProps): import("react/jsx-runtime").JSX.Element;
|
|
8
8
|
Header: import("react").ForwardRefExoticComponent<import("./Header").HeaderProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
9
9
|
Sidebar: import("react").ForwardRefExoticComponent<import("../DashboardSidebar").DashboardSidebarProps & import("react").RefAttributes<HTMLBaseElement>>;
|
|
10
10
|
Main: import("react").ForwardRefExoticComponent<import("./Main").MainProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
@@ -7,8 +7,8 @@ import { Main } from './Main';
|
|
|
7
7
|
/**
|
|
8
8
|
* Общий Layout приложения
|
|
9
9
|
*/
|
|
10
|
-
export const DashboardLayout = ({ children, isFocusedMode = false, isLoading = false, }) => {
|
|
11
|
-
return (_jsx(DashboardContextProvider, { isFocusedMode: isFocusedMode, isLoading: isLoading, children: _jsx(DashboardWrapper, { children: children }) }));
|
|
10
|
+
export const DashboardLayout = ({ children, isFocusedMode = false, isLoading = false, mobileHeaderPriorityFeature = 'menuOrg', }) => {
|
|
11
|
+
return (_jsx(DashboardContextProvider, { isFocusedMode: isFocusedMode, isLoading: isLoading, mobileHeaderPriorityFeature: mobileHeaderPriorityFeature, children: _jsx(DashboardWrapper, { children: children }) }));
|
|
12
12
|
};
|
|
13
13
|
DashboardLayout.Header = Header;
|
|
14
14
|
DashboardLayout.Sidebar = DashboardSidebar;
|
|
@@ -6,15 +6,40 @@ import { QuitOutlineMd } from '../../../icons/QuitOutlineMd';
|
|
|
6
6
|
import { Button } from '../../Button';
|
|
7
7
|
import { Product } from '../../Product';
|
|
8
8
|
import { Profile } from '../../Profile';
|
|
9
|
+
import { cva } from '../../utils/cva';
|
|
9
10
|
import { getInertProps } from '../../utils/getInertProps';
|
|
10
11
|
import { dashboardLayoutHeaderClassnames } from './constants';
|
|
11
|
-
import { ExitButton, HeaderContent, HeaderContentSection, HeaderRoot, HeaderSection,
|
|
12
|
+
import { ExitButton, HeaderContent, HeaderContentSection, HeaderRoot, HeaderSection, MobileSidebarTogglerWrapper, PriorityFeatureWrapper, ProductSwitcherWrapper, ProfileWrapper, } from './styles';
|
|
12
13
|
import { useLogic } from './useLogic';
|
|
14
|
+
const headerPriorityFeature = cva('', {
|
|
15
|
+
variants: {
|
|
16
|
+
feature: {
|
|
17
|
+
menuOrg: dashboardLayoutHeaderClassnames.priorityFeatureMenuOrg,
|
|
18
|
+
profile: dashboardLayoutHeaderClassnames.priorityFeatureProfile,
|
|
19
|
+
},
|
|
20
|
+
isPinned: {
|
|
21
|
+
true: dashboardLayoutHeaderClassnames.mobileSidebarPriorityFeatureVisible,
|
|
22
|
+
},
|
|
23
|
+
featureInSidebar: {
|
|
24
|
+
true: dashboardLayoutHeaderClassnames.featureInSidebar,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
});
|
|
13
28
|
/**
|
|
14
29
|
* Основной header приложения
|
|
15
30
|
*/
|
|
16
31
|
export const Header = forwardRef((props, ref) => {
|
|
17
32
|
const { productSwitcher: ProductSwitcher, product, profile, menuOrganization, children, } = props;
|
|
18
|
-
const { isShowExitButton, isShowProfile, isFocusedMode, isMobile, collapsedIn, onToggleSidebar, isLoading,
|
|
19
|
-
return (_jsx(HeaderRoot, { ref: ref, "$isFocusedMode": isFocusedMode, ...getInertProps(!isMobile && isFocusedMode), className: dashboardLayoutHeaderClassnames.root, children: _jsxs(HeaderContent, { children: [_jsx(MobileSidebarTogglerWrapper, { className: dashboardLayoutHeaderClassnames.mobileSidebarButton, children: _jsx(Button, { startIcon: collapsedIn ? _jsx(CrossOutlineMd, {}) : _jsx(MenuOnOutlineMd, {}), variant: "text", onClick: () => onToggleSidebar(), title: "\u041F\u0435\u0440\u0435\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0431\u043E\u043A\u043E\u0432\u043E\u0435 \u043C\u0435\u043D\u044E" }) }), _jsxs(HeaderSection, { children: [ProductSwitcher && (_jsx(ProductSwitcherWrapper, { children: _jsx(ProductSwitcher, {}) })), _jsx(Product, { ...product })] }), _jsxs(HeaderContentSection, {
|
|
33
|
+
const { isShowExitButton, isShowProfile, isFocusedMode, isMobile, collapsedIn, onToggleSidebar, isLoading, mobileHeaderPriorityFeature, headerStyle, isPinned, } = useLogic(props);
|
|
34
|
+
return (_jsx(HeaderRoot, { ref: ref, "$isFocusedMode": isFocusedMode, ...getInertProps(!isMobile && isFocusedMode), className: dashboardLayoutHeaderClassnames.root, style: headerStyle, children: _jsxs(HeaderContent, { children: [_jsx(MobileSidebarTogglerWrapper, { className: dashboardLayoutHeaderClassnames.mobileSidebarButton, children: _jsx(Button, { startIcon: collapsedIn ? _jsx(CrossOutlineMd, {}) : _jsx(MenuOnOutlineMd, {}), variant: "text", onClick: () => onToggleSidebar(), title: "\u041F\u0435\u0440\u0435\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0431\u043E\u043A\u043E\u0432\u043E\u0435 \u043C\u0435\u043D\u044E" }) }), _jsxs(HeaderSection, { children: [ProductSwitcher && (_jsx(ProductSwitcherWrapper, { children: _jsx(ProductSwitcher, {}) })), _jsx(Product, { ...product })] }), _jsxs(HeaderContentSection, { className: headerPriorityFeature({
|
|
35
|
+
feature: mobileHeaderPriorityFeature,
|
|
36
|
+
}), children: [children, menuOrganization && (_jsx(PriorityFeatureWrapper, { className: headerPriorityFeature({
|
|
37
|
+
feature: mobileHeaderPriorityFeature,
|
|
38
|
+
isPinned,
|
|
39
|
+
featureInSidebar: mobileHeaderPriorityFeature === 'profile',
|
|
40
|
+
}), children: menuOrganization() })), profile && (_jsx(PriorityFeatureWrapper, { className: headerPriorityFeature({
|
|
41
|
+
feature: mobileHeaderPriorityFeature,
|
|
42
|
+
isPinned,
|
|
43
|
+
featureInSidebar: mobileHeaderPriorityFeature === 'menuOrg',
|
|
44
|
+
}), children: _jsx(ProfileWrapper, { className: dashboardLayoutHeaderClassnames.profile, "$isShow": isShowProfile, children: _jsx(Profile, { isLoading: isLoading, ...profile }) }) })), _jsx(ExitButton, { "$isShow": isShowExitButton, onClick: profile?.exitButton?.onClick, title: "\u0412\u044B\u0445\u043E\u0434", variant: "text", children: _jsx(QuitOutlineMd, {}) })] })] }) }));
|
|
20
45
|
});
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
export declare const dashboardLayoutHeaderClassnames: {
|
|
2
2
|
root: string;
|
|
3
3
|
mobileSidebarButton: string;
|
|
4
|
-
|
|
4
|
+
priorityFeatureMenuOrg: string;
|
|
5
|
+
priorityFeatureProfile: string;
|
|
6
|
+
mobileSidebarPriorityFeatureVisible: string;
|
|
7
|
+
featureInSidebar: string;
|
|
8
|
+
profile: string;
|
|
5
9
|
};
|
|
@@ -2,5 +2,9 @@ import { createUIKitClassname } from '../../utils/createUIKitClassname';
|
|
|
2
2
|
export const dashboardLayoutHeaderClassnames = {
|
|
3
3
|
root: createUIKitClassname('dashboard-layout-header'),
|
|
4
4
|
mobileSidebarButton: createUIKitClassname('dashboard-layout-header__sidebar-button_mobile'),
|
|
5
|
-
|
|
5
|
+
priorityFeatureMenuOrg: createUIKitClassname('dashboard-layout-header__priority-feature_menu-org'),
|
|
6
|
+
priorityFeatureProfile: createUIKitClassname('dashboard-layout-header__priority-feature_profile'),
|
|
7
|
+
mobileSidebarPriorityFeatureVisible: createUIKitClassname('dashboard-layout-header__sidebar-priority-feature_visible'),
|
|
8
|
+
featureInSidebar: createUIKitClassname('dashboard-layout-header__feature-in-sidebar'),
|
|
9
|
+
profile: createUIKitClassname('dashboard-layout-header__profile'),
|
|
6
10
|
};
|
|
@@ -44,9 +44,10 @@ export declare const HeaderContent: import("../../styled").StyledComponent<{
|
|
|
44
44
|
theme?: import("@emotion/react").Theme | undefined;
|
|
45
45
|
as?: import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements> | undefined;
|
|
46
46
|
}, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
|
|
47
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Обертка для элементов которые должны быть в мобильном хедере или помещены в sidebar.
|
|
49
|
+
*/
|
|
50
|
+
export declare const PriorityFeatureWrapper: import("../../styled").StyledComponent<{
|
|
48
51
|
theme?: import("@emotion/react").Theme | undefined;
|
|
49
52
|
as?: import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements> | undefined;
|
|
50
|
-
} & {
|
|
51
|
-
$alertHeight?: number | undefined;
|
|
52
53
|
}, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
|
|
@@ -3,6 +3,7 @@ import { dashboardSidebarClassnames } from '../../DashboardSidebar/constants';
|
|
|
3
3
|
import { IconButton } from '../../IconButton';
|
|
4
4
|
import { menuOrganizationClassnames } from '../../MenuOrganization/constants';
|
|
5
5
|
import { MOBILE_BUTTON_HEIGHT } from '../../MenuOrganization/OrganizationButton/constants';
|
|
6
|
+
import { profileClassnames } from '../../Profile/constants';
|
|
6
7
|
import { styled } from '../../styled';
|
|
7
8
|
import { dashboardLayoutClassnames, HEADER_HEIGHT_DESKTOP, HEADER_HEIGHT_LAPTOP, HEADER_HEIGHT_MOBILE, } from '../constants';
|
|
8
9
|
import { dashboardLayoutHeaderClassnames } from './constants';
|
|
@@ -77,6 +78,15 @@ export const HeaderContentSection = styled(HeaderSection) `
|
|
|
77
78
|
|
|
78
79
|
${({ theme }) => theme.breakpoints.down('sm')} {
|
|
79
80
|
gap: ${({ theme }) => theme.spacing(2)};
|
|
81
|
+
|
|
82
|
+
&.${dashboardLayoutHeaderClassnames.priorityFeatureMenuOrg} {
|
|
83
|
+
& .${menuOrganizationClassnames.organizationData} {
|
|
84
|
+
display: none;
|
|
85
|
+
}
|
|
86
|
+
& .${menuOrganizationClassnames.button} {
|
|
87
|
+
border-radius: ${({ theme }) => theme.shape.medium};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
80
90
|
}
|
|
81
91
|
`;
|
|
82
92
|
export const ProductSwitcherWrapper = styled.div `
|
|
@@ -111,45 +121,77 @@ export const HeaderContent = styled.div `
|
|
|
111
121
|
padding: 0;
|
|
112
122
|
}
|
|
113
123
|
`;
|
|
114
|
-
|
|
124
|
+
/**
|
|
125
|
+
* Обертка для элементов которые должны быть в мобильном хедере или помещены в sidebar.
|
|
126
|
+
*/
|
|
127
|
+
export const PriorityFeatureWrapper = styled.div `
|
|
115
128
|
${({ theme }) => theme.breakpoints.down('sm')} {
|
|
116
|
-
position: absolute;
|
|
117
|
-
z-index: ${({ theme }) => theme.zIndex.appBar - 1};
|
|
118
|
-
top: ${({ $alertHeight }) => `calc(${$alertHeight}px + ${HEADER_HEIGHT_MOBILE}) `};
|
|
119
|
-
left: 0;
|
|
120
|
-
|
|
121
|
-
/* Необходимо для анимации компонента вместе с sidebar */
|
|
122
|
-
transform: translateX(-100vw);
|
|
123
129
|
|
|
124
|
-
|
|
125
|
-
|
|
130
|
+
&.${dashboardLayoutHeaderClassnames.featureInSidebar} {
|
|
131
|
+
position: absolute;
|
|
132
|
+
z-index: ${({ theme }) => theme.zIndex.appBar - 1};
|
|
133
|
+
top: calc(var(--alert-height, 0px) + ${HEADER_HEIGHT_MOBILE});
|
|
134
|
+
left: 0;
|
|
135
|
+
|
|
136
|
+
/* Необходимо для анимации компонента вместе с sidebar */
|
|
137
|
+
transform: translateX(-100vw);
|
|
126
138
|
|
|
127
|
-
|
|
139
|
+
display: flex;
|
|
140
|
+
flex-direction: column;
|
|
128
141
|
|
|
129
|
-
|
|
142
|
+
width: 100vw;
|
|
130
143
|
|
|
131
|
-
|
|
144
|
+
background-color: ${({ theme }) => theme.palette.background.default};
|
|
145
|
+
|
|
146
|
+
transition: ${({ theme }) => {
|
|
132
147
|
return theme.transitions.create(['transform'], {
|
|
133
148
|
duration: theme.transitions.duration.standard,
|
|
134
149
|
});
|
|
135
150
|
}};
|
|
136
151
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
& .${menuOrganizationClassnames.button} {
|
|
142
|
-
height: ${MOBILE_BUTTON_HEIGHT};
|
|
143
|
-
padding: ${({ theme }) => theme.spacing(3, 4)};
|
|
152
|
+
&.${dashboardLayoutHeaderClassnames.mobileSidebarPriorityFeatureVisible} {
|
|
153
|
+
transform: translateX(0);
|
|
154
|
+
}
|
|
144
155
|
|
|
145
|
-
|
|
156
|
+
& .${menuOrganizationClassnames.button}, & .${profileClassnames.button} {
|
|
157
|
+
height: ${MOBILE_BUTTON_HEIGHT};
|
|
158
|
+
padding: ${({ theme }) => theme.spacing(3, 4)};
|
|
146
159
|
|
|
147
|
-
&:hover {
|
|
148
160
|
border-bottom: 1px solid ${({ theme }) => theme.palette.grey[300]};
|
|
161
|
+
|
|
162
|
+
&:hover {
|
|
163
|
+
border-bottom: 1px solid ${({ theme }) => theme.palette.grey[300]};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
&:active {
|
|
167
|
+
border-bottom: 1px solid ${({ theme }) => theme.palette.grey[300]};
|
|
168
|
+
}
|
|
149
169
|
}
|
|
150
170
|
|
|
151
|
-
|
|
152
|
-
|
|
171
|
+
&.${dashboardLayoutHeaderClassnames.priorityFeatureMenuOrg} {
|
|
172
|
+
& .${dashboardLayoutHeaderClassnames.profile} {
|
|
173
|
+
display: block;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
& .${profileClassnames.button} {
|
|
178
|
+
width: 100%;
|
|
179
|
+
max-width: unset;
|
|
180
|
+
|
|
181
|
+
border-radius: 0;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
& .${profileClassnames.avatar} {
|
|
185
|
+
display: none;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
& .${profileClassnames.user} {
|
|
189
|
+
display: flex;
|
|
190
|
+
align-items: center;
|
|
191
|
+
justify-content: space-between;
|
|
192
|
+
|
|
193
|
+
width: 100%;
|
|
194
|
+
}
|
|
153
195
|
}
|
|
154
196
|
}
|
|
155
197
|
}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
+
import { type CSSProperties } from 'react';
|
|
1
2
|
import { type HeaderProps } from '../types';
|
|
2
3
|
export declare const useLogic: ({ profile, menuOrganization }: HeaderProps) => {
|
|
3
4
|
isShowExitButton: boolean;
|
|
4
5
|
isShowProfile: boolean;
|
|
5
6
|
isMobile: boolean;
|
|
6
7
|
isFocusedMode: boolean;
|
|
8
|
+
mobileHeaderPriorityFeature: "profile" | "menuOrg";
|
|
7
9
|
isLoading: boolean;
|
|
8
10
|
collapsedIn: boolean;
|
|
9
11
|
onToggleSidebar: (newValue?: boolean | undefined) => void;
|
|
10
12
|
alertHeight: number;
|
|
11
|
-
|
|
13
|
+
isPinned: boolean;
|
|
14
|
+
headerStyle: CSSProperties;
|
|
12
15
|
};
|
|
@@ -1,31 +1,36 @@
|
|
|
1
|
-
import { useContext, useEffect
|
|
1
|
+
import { useContext, useEffect } from 'react';
|
|
2
2
|
import { DashboardContext } from '../../../DashboardContext';
|
|
3
3
|
import { DashboardSidebarContext } from '../../../DashboardSidebarProvider';
|
|
4
4
|
import { useViewportType } from '../../../useViewportType';
|
|
5
|
-
import { classNames } from '../../../utils/classNames';
|
|
6
|
-
import { dashboardLayoutHeaderClassnames } from '../constants';
|
|
7
5
|
export const useLogic = ({ profile, menuOrganization }) => {
|
|
8
|
-
const { isFocusedMode, isLoading, hasMenuOrganizationRef, alertHeight } = useContext(DashboardContext);
|
|
6
|
+
const { isFocusedMode, isLoading, hasMenuOrganizationRef, hasProfileRef, alertHeight, mobileHeaderPriorityFeature, } = useContext(DashboardContext);
|
|
9
7
|
const { collapsedIn, onToggleSidebar, isPinned } = useContext(DashboardSidebarContext);
|
|
10
8
|
useEffect(() => {
|
|
11
9
|
hasMenuOrganizationRef.current = Boolean(menuOrganization);
|
|
12
|
-
|
|
10
|
+
hasProfileRef.current = Boolean(profile);
|
|
11
|
+
}, [menuOrganization, profile]);
|
|
13
12
|
const { isMobile } = useViewportType();
|
|
14
13
|
const hasMenu = Boolean(profile?.menu || profile?.menuList);
|
|
15
|
-
const isShowProfile = (Boolean(profile) && !isMobile) ||
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
const isShowProfile = (Boolean(profile) && !isMobile) ||
|
|
15
|
+
(hasMenu && isMobile && mobileHeaderPriorityFeature === 'profile');
|
|
16
|
+
const isShowExitButton = isMobile &&
|
|
17
|
+
!hasMenu &&
|
|
18
|
+
Boolean(profile?.exitButton) &&
|
|
19
|
+
mobileHeaderPriorityFeature === 'profile';
|
|
20
|
+
const headerStyle = {
|
|
21
|
+
'--alert-height': `${alertHeight}px`,
|
|
22
|
+
};
|
|
20
23
|
return {
|
|
21
24
|
isShowExitButton,
|
|
22
25
|
isShowProfile,
|
|
23
26
|
isMobile,
|
|
24
27
|
isFocusedMode,
|
|
28
|
+
mobileHeaderPriorityFeature,
|
|
25
29
|
isLoading,
|
|
26
30
|
collapsedIn,
|
|
27
31
|
onToggleSidebar,
|
|
28
32
|
alertHeight,
|
|
29
|
-
|
|
33
|
+
isPinned,
|
|
34
|
+
headerStyle,
|
|
30
35
|
};
|
|
31
36
|
};
|
|
@@ -10,4 +10,9 @@ export type DashboardLayoutProps = {
|
|
|
10
10
|
* Если true, отображается состояние загрузки
|
|
11
11
|
*/
|
|
12
12
|
isLoading?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Элемент, отображаемый в мобильном хедере.
|
|
15
|
+
* Второстепенный будет помещен в sidebar.
|
|
16
|
+
*/
|
|
17
|
+
mobileHeaderPriorityFeature?: 'profile' | 'menuOrg';
|
|
13
18
|
};
|
|
@@ -3,17 +3,32 @@ import { forwardRef } from 'react';
|
|
|
3
3
|
import { DashboardSidebarSkeleton } from '../DashboardSidebarSkeleton';
|
|
4
4
|
import { NavMenu } from '../NavMenu';
|
|
5
5
|
import { PinButton } from '../PinButton';
|
|
6
|
+
import { cva } from '../utils/cva';
|
|
6
7
|
import { getInertProps } from '../utils/getInertProps';
|
|
8
|
+
import { dashboardSidebarClassnames } from './constants';
|
|
7
9
|
import { SidebarNav } from './SidebarNav';
|
|
8
10
|
import { Footer, SidebarContent, SidebarHeader, SidebarRoot, StyledPaper, Wrapper, } from './styles';
|
|
9
11
|
import { useLogic } from './useLogic';
|
|
12
|
+
const sidebar = cva('', {
|
|
13
|
+
variants: {
|
|
14
|
+
hasProfile: {
|
|
15
|
+
true: dashboardSidebarClassnames.hasProfile,
|
|
16
|
+
},
|
|
17
|
+
hasMenuOrganization: {
|
|
18
|
+
true: dashboardSidebarClassnames.hasMenuOrganization,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
});
|
|
10
22
|
/**
|
|
11
23
|
* Основной sidebar приложения
|
|
12
24
|
*/
|
|
13
25
|
export const DashboardSidebar = forwardRef((props, ref) => {
|
|
14
|
-
const { isPinned, isMobile, collapsedIn, onTogglePin, onMouseEnter, onMouseLeave, alertHeight, classnames,
|
|
26
|
+
const { isPinned, isMobile, collapsedIn, onTogglePin, onMouseEnter, onMouseLeave, alertHeight, classnames, menu, header, isLoading, hasMenuOrganization, hasProfile, } = useLogic(props);
|
|
15
27
|
if (isLoading) {
|
|
16
28
|
return _jsx(DashboardSidebarSkeleton, {});
|
|
17
29
|
}
|
|
18
|
-
return (_jsx(Wrapper, { "$isPinned": isPinned, "$alertHeight": alertHeight, className:
|
|
30
|
+
return (_jsx(Wrapper, { "$isPinned": isPinned, "$alertHeight": alertHeight, className: sidebar({
|
|
31
|
+
hasProfile,
|
|
32
|
+
hasMenuOrganization,
|
|
33
|
+
}), children: _jsx(SidebarRoot, { ref: ref, "$isPinned": isPinned, "$collapsedIn": collapsedIn, className: classnames, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, ...getInertProps(isMobile && !isPinned), children: _jsxs(StyledPaper, { variant: "outlined", children: [_jsxs(SidebarContent, { "$collapsedIn": collapsedIn, "$isPinned": isPinned, "$hasHeader": Boolean(header), children: [header && _jsx(SidebarHeader, { children: header }), _jsx(SidebarNav, { menu: _jsx(NavMenu, { collapsedIn: collapsedIn, items: menu.items }) })] }), _jsx(Footer, { children: _jsx(PinButton, { isPinned: isPinned, collapsedIn: collapsedIn, onClick: onTogglePin }) })] }) }) }));
|
|
19
34
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createUIKitClassname } from '../utils/createUIKitClassname';
|
|
2
2
|
export const dashboardSidebarClassnames = {
|
|
3
3
|
root: createUIKitClassname('dashboard-layout-sidebar'),
|
|
4
|
+
hasProfile: createUIKitClassname('dashboard-layout-sidebar_has-profile'),
|
|
4
5
|
hasMenuOrganization: createUIKitClassname('dashboard-layout-sidebar_has-menu-organization'),
|
|
5
6
|
};
|
|
6
7
|
export const LABEL_WIDTH = {
|
|
@@ -35,7 +35,7 @@ export const Wrapper = styled('div', {
|
|
|
35
35
|
});
|
|
36
36
|
}};
|
|
37
37
|
|
|
38
|
-
&.${dashboardSidebarClassnames.hasMenuOrganization} {
|
|
38
|
+
&.${dashboardSidebarClassnames.hasMenuOrganization}, &.${dashboardSidebarClassnames.hasProfile} {
|
|
39
39
|
top: ${({ $alertHeight }) => `calc(${$alertHeight}px + ${HEADER_HEIGHT_MOBILE} + ${MOBILE_BUTTON_HEIGHT}) `};
|
|
40
40
|
}
|
|
41
41
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useContext, useEffect,
|
|
1
|
+
import { useContext, useEffect, useRef } from 'react';
|
|
2
2
|
import { DashboardContext } from '../../DashboardContext';
|
|
3
3
|
import { dashboardLayoutHeaderClassnames } from '../../DashboardLayout/Header';
|
|
4
4
|
import { dashboardLayoutMainClassnames } from '../../DashboardLayout/Main';
|
|
@@ -10,7 +10,7 @@ import { dashboardSidebarClassnames } from '../constants';
|
|
|
10
10
|
export const useLogic = (props) => {
|
|
11
11
|
const { className, menu, header, isLoading } = props;
|
|
12
12
|
const { onToggleSidebar, isPinned, onTogglePin, collapsedIn, setIsPopupOpen, } = useContext(DashboardSidebarContext);
|
|
13
|
-
const { alertHeight, isLoading: isDashboardLoading, hasMenuOrganizationRef, } = useContext(DashboardContext);
|
|
13
|
+
const { alertHeight, isLoading: isDashboardLoading, hasMenuOrganizationRef, hasProfileRef, mobileHeaderPriorityFeature, } = useContext(DashboardContext);
|
|
14
14
|
const { isMobile } = useViewportType();
|
|
15
15
|
const hoverTimerRef = useRef(null);
|
|
16
16
|
const theme = useTheme();
|
|
@@ -80,9 +80,6 @@ export const useLogic = (props) => {
|
|
|
80
80
|
}
|
|
81
81
|
};
|
|
82
82
|
}, [collapsedIn, isPinned]);
|
|
83
|
-
const wrapperClassnames = useMemo(() => classNames({
|
|
84
|
-
[dashboardSidebarClassnames.hasMenuOrganization]: hasMenuOrganizationRef.current,
|
|
85
|
-
}), [hasMenuOrganizationRef.current]);
|
|
86
83
|
const classnames = classNames(className, dashboardSidebarClassnames.root);
|
|
87
84
|
return {
|
|
88
85
|
isPinned,
|
|
@@ -96,6 +93,8 @@ export const useLogic = (props) => {
|
|
|
96
93
|
header,
|
|
97
94
|
isLoading: isLoading || isDashboardLoading,
|
|
98
95
|
classnames,
|
|
99
|
-
|
|
96
|
+
hasMenuOrganization: hasMenuOrganizationRef.current &&
|
|
97
|
+
mobileHeaderPriorityFeature === 'profile',
|
|
98
|
+
hasProfile: hasProfileRef.current && mobileHeaderPriorityFeature === 'menuOrg',
|
|
100
99
|
};
|
|
101
100
|
};
|
|
@@ -30,6 +30,8 @@ type UseMaskedValueAndSelectedBaseDateReturn = {
|
|
|
30
30
|
*/
|
|
31
31
|
value: string;
|
|
32
32
|
};
|
|
33
|
+
/** При blur неполной даты - в onChange уходит Invalid Date (тот же стейт, что у маски). */
|
|
34
|
+
onMaskedValueBlur: () => void;
|
|
33
35
|
};
|
|
34
36
|
/**
|
|
35
37
|
* хук объединяющий повторяющуюся логику в работе DatePicker и RangeDatePicker:
|
|
@@ -12,7 +12,7 @@ import { useSelectedBaseDate } from '../useSelectedBaseDate';
|
|
|
12
12
|
*/
|
|
13
13
|
export const useDatePickerOptions = ({ onChange, mask, currentValue, minDate, maxDate, monthOffset, onDatePick, }) => {
|
|
14
14
|
const baseDate = useBaseDateInRange({ minDate, maxDate, monthOffset });
|
|
15
|
-
const { maskedValue, onMaskedValueChange, onMaskedDateChange } = useMaskedValue({
|
|
15
|
+
const { maskedValue, onMaskedValueChange, onMaskedValueBlur, onMaskedDateChange, } = useMaskedValue({
|
|
16
16
|
currentValue,
|
|
17
17
|
mask,
|
|
18
18
|
onChangeValue: onChange,
|
|
@@ -39,5 +39,6 @@ export const useDatePickerOptions = ({ onChange, mask, currentValue, minDate, ma
|
|
|
39
39
|
onChange: handleDatePick,
|
|
40
40
|
date: selectedBaseDate || baseDate,
|
|
41
41
|
},
|
|
42
|
+
onMaskedValueBlur,
|
|
42
43
|
};
|
|
43
44
|
};
|
|
@@ -1,24 +1,48 @@
|
|
|
1
1
|
import { isDate } from '@astral/utils/date/isDate';
|
|
2
|
-
import { useEffect, useState } from 'react';
|
|
3
|
-
import { formatDate, parseDate } from '../../../utils/date';
|
|
2
|
+
import { useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { formatDate, isMaskedDateSyntacticallyComplete, parseDate, } from '../../../utils/date';
|
|
4
4
|
/**
|
|
5
5
|
* Хук для управления значением для MaskField
|
|
6
6
|
*/
|
|
7
7
|
export const useMaskedValue = ({ currentValue, mask, onChangeValue, }) => {
|
|
8
8
|
const [maskedValue, setMaskedValue] = useState(() => currentValue ? formatDate(currentValue, mask) : '');
|
|
9
|
+
const maskedValueRef = useRef(maskedValue);
|
|
10
|
+
/**
|
|
11
|
+
* Ввод в маску: синхронизируем локальное значение и ref (ref нужен на blur и после выбора даты из календаря,
|
|
12
|
+
* чтобы не читать устаревший state).
|
|
13
|
+
* `onChangeValue` вызываем только при пустой строке (`null`) или когда маска синтаксически полная —
|
|
14
|
+
* иначе partial не уходит наружу до завершения ввода или до blur (см. `handleMaskedValueBlur`).
|
|
15
|
+
*/
|
|
9
16
|
const handleMaskedValueChange = (value) => {
|
|
10
17
|
setMaskedValue(value);
|
|
11
|
-
|
|
18
|
+
maskedValueRef.current = value;
|
|
12
19
|
if (!value) {
|
|
13
20
|
onChangeValue?.(null, value);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (!isMaskedDateSyntacticallyComplete(value, mask)) {
|
|
24
|
+
return;
|
|
14
25
|
}
|
|
15
|
-
|
|
16
|
-
|
|
26
|
+
onChangeValue?.(parseDate(value, mask), value);
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Blur поля: если осталась непустая неполная маска — один раз отдаём `Invalid Date` в `onChangeValue`,
|
|
30
|
+
* чтобы формы могли показать ошибку. Пустая строка и полная маска не дублируют уже отправленный `onChange`.
|
|
31
|
+
*/
|
|
32
|
+
const handleMaskedValueBlur = () => {
|
|
33
|
+
const currentMaskedValue = maskedValueRef.current;
|
|
34
|
+
if (!currentMaskedValue) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (isMaskedDateSyntacticallyComplete(currentMaskedValue, mask)) {
|
|
38
|
+
return;
|
|
17
39
|
}
|
|
40
|
+
onChangeValue?.(new Date(Number.NaN), currentMaskedValue);
|
|
18
41
|
};
|
|
19
42
|
const handleChangeMaskedDate = (date) => {
|
|
20
43
|
const formatted = formatDate(date, mask);
|
|
21
44
|
setMaskedValue(formatted);
|
|
45
|
+
maskedValueRef.current = formatted;
|
|
22
46
|
onChangeValue?.(date, formatted);
|
|
23
47
|
};
|
|
24
48
|
// здесь происходит реакция на изменение value из вне (управляемый компонент)
|
|
@@ -26,6 +50,7 @@ export const useMaskedValue = ({ currentValue, mask, onChangeValue, }) => {
|
|
|
26
50
|
// если новое значение пустое, то сбрасываем значение MaskField
|
|
27
51
|
if (!currentValue) {
|
|
28
52
|
setMaskedValue('');
|
|
53
|
+
maskedValueRef.current = '';
|
|
29
54
|
return;
|
|
30
55
|
}
|
|
31
56
|
// здесь обрабатывается сценарий, когда в инпут вводится невалидная дата и при этом currentValue становится Invalid Date
|
|
@@ -33,16 +58,20 @@ export const useMaskedValue = ({ currentValue, mask, onChangeValue, }) => {
|
|
|
33
58
|
if (!isDate(currentValue) && maskedValue) {
|
|
34
59
|
return;
|
|
35
60
|
}
|
|
36
|
-
//
|
|
37
|
-
const isEqualValueAndMaskedDate =
|
|
61
|
+
// сравнение только при полной маске, иначе getTime() от parseDate может быть NaN
|
|
62
|
+
const isEqualValueAndMaskedDate = isMaskedDateSyntacticallyComplete(maskedValue, mask) &&
|
|
63
|
+
currentValue.getTime() === parseDate(maskedValue, mask).getTime();
|
|
38
64
|
// если даты не равны, то значит изменился currentValue из вне и надо синхронизировать maskedValue
|
|
39
65
|
if (!isEqualValueAndMaskedDate) {
|
|
40
|
-
|
|
66
|
+
const formatted = formatDate(currentValue, mask);
|
|
67
|
+
setMaskedValue(formatted);
|
|
68
|
+
maskedValueRef.current = formatted;
|
|
41
69
|
}
|
|
42
70
|
}, [currentValue]);
|
|
43
71
|
return {
|
|
44
72
|
maskedValue,
|
|
45
73
|
onMaskedValueChange: handleMaskedValueChange,
|
|
74
|
+
onMaskedValueBlur: handleMaskedValueBlur,
|
|
46
75
|
onMaskedDateChange: handleChangeMaskedDate,
|
|
47
76
|
};
|
|
48
77
|
};
|
|
@@ -13,6 +13,7 @@ export declare const useLogic: ({ label, value, maxDate, minDate, mask, onOpen,
|
|
|
13
13
|
};
|
|
14
14
|
DatePickerInputProps: {
|
|
15
15
|
value: string;
|
|
16
|
+
onBlur: () => void;
|
|
16
17
|
};
|
|
17
18
|
onAccept: ((value: string, maskRef: import("imask").default.InputMask<import("imask").default.AnyMaskedOptions>, e?: InputEvent | undefined, onChange?: ((changeValue: string) => void) | undefined) => void) | undefined;
|
|
18
19
|
pickerProps: import("../types").PickerProps;
|