@os-design/core 1.0.199 → 1.0.200
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/package.json +21 -13
- package/src/@types/emotion.d.ts +7 -0
- package/src/Alert/index.tsx +112 -0
- package/src/Avatar/index.tsx +173 -0
- package/src/Avatar/utils/nameToInitials.ts +12 -0
- package/src/Avatar/utils/strToHue.ts +13 -0
- package/src/AvatarSkeleton/index.tsx +29 -0
- package/src/Breadcrumb/index.tsx +93 -0
- package/src/BreadcrumbItem/index.tsx +83 -0
- package/src/Button/ButtonContent.tsx +91 -0
- package/src/Button/index.tsx +225 -0
- package/src/Button/utils/useButtonColors.ts +84 -0
- package/src/Checkbox/index.tsx +225 -0
- package/src/CheckboxSkeleton/index.tsx +50 -0
- package/src/DatePicker/DatePickerCalendar.tsx +220 -0
- package/src/DatePicker/index.tsx +568 -0
- package/src/Drawer/index.tsx +212 -0
- package/src/Form/FormConfigContext.ts +16 -0
- package/src/Form/index.tsx +49 -0
- package/src/FormDivider/index.tsx +74 -0
- package/src/FormItem/index.tsx +118 -0
- package/src/Gallery/Status.tsx +62 -0
- package/src/Gallery/index.tsx +290 -0
- package/src/GlobalStyles/index.tsx +17 -0
- package/src/GlobalStyles/resetStyles.ts +17 -0
- package/src/GlobalStyles/typographyStyles.ts +78 -0
- package/src/HeaderSkeleton/index.tsx +64 -0
- package/src/Image/index.tsx +104 -0
- package/src/ImageSkeleton/index.tsx +22 -0
- package/src/Input/index.tsx +330 -0
- package/src/Input/utils/getFocusableElements.ts +8 -0
- package/src/InputNumber/index.tsx +208 -0
- package/src/InputNumber/utils/defaultLocale.ts +9 -0
- package/src/InputPassword/index.tsx +201 -0
- package/src/InputPassword/utils/defaultLocale.ts +11 -0
- package/src/InputSearch/index.tsx +111 -0
- package/src/InputSearch/utils/defaultLocale.ts +9 -0
- package/src/InputSkeleton/index.tsx +28 -0
- package/src/Layout/LayoutContext.ts +21 -0
- package/src/Layout/index.tsx +44 -0
- package/src/Link/index.tsx +129 -0
- package/src/LinkButton/index.tsx +100 -0
- package/src/List/WindowScroller.tsx +53 -0
- package/src/List/index.tsx +255 -0
- package/src/List/utils/bodyPointerEvents.ts +24 -0
- package/src/List/utils/frameTimeout.ts +36 -0
- package/src/List/utils/useRWLoadNext.ts +38 -0
- package/src/ListItem/index.tsx +92 -0
- package/src/ListItemActions/index.tsx +207 -0
- package/src/ListItemLink/index.tsx +63 -0
- package/src/ListSkeleton/index.tsx +115 -0
- package/src/LogoLink/index.tsx +93 -0
- package/src/LogoLink/logo.example.svg +18 -0
- package/src/Menu/index.tsx +128 -0
- package/src/Menu/utils/useFocusWithArrows.ts +50 -0
- package/src/MenuDivider/index.tsx +22 -0
- package/src/MenuGroup/index.tsx +190 -0
- package/src/MenuItem/index.tsx +108 -0
- package/src/Modal/index.tsx +411 -0
- package/src/Modal/utils/defaultLocale.ts +9 -0
- package/src/Navigation/index.tsx +214 -0
- package/src/Navigation/utils/useScrollFlags.ts +39 -0
- package/src/NavigationItem/index.tsx +136 -0
- package/src/PageContent/index.tsx +99 -0
- package/src/PageHeader/index.tsx +246 -0
- package/src/PageHeader/utils/defaultLocale.ts +9 -0
- package/src/PageHeaderInputSearch/index.tsx +145 -0
- package/src/PageHeaderInputSearch/utils/defaultLocale.ts +16 -0
- package/src/PageHeaderSkeleton/index.tsx +33 -0
- package/src/ParagraphSkeleton/index.tsx +65 -0
- package/src/Popover/index.tsx +243 -0
- package/src/Popover/utils/usePopoverPosition.ts +216 -0
- package/src/Progress/index.tsx +100 -0
- package/src/RadioGroup/index.tsx +165 -0
- package/src/RadioGroupSkeleton/index.tsx +36 -0
- package/src/Result/index.tsx +109 -0
- package/src/ScrollButton/index.tsx +159 -0
- package/src/ScrollButton/utils/useContainerPosition.ts +41 -0
- package/src/ScrollButton/utils/useVisibility.ts +56 -0
- package/src/Select/index.tsx +970 -0
- package/src/Select/utils/defaultLocale.ts +11 -0
- package/src/Skeleton/index.tsx +52 -0
- package/src/Switch/index.tsx +217 -0
- package/src/SwitchSkeleton/index.tsx +30 -0
- package/src/Tag/index.tsx +75 -0
- package/src/TagLink/index.tsx +53 -0
- package/src/TagList/index.tsx +95 -0
- package/src/TagListSkeleton/index.tsx +38 -0
- package/src/TagSkeleton/index.tsx +40 -0
- package/src/TextArea/index.tsx +231 -0
- package/src/TextAreaSkeleton/index.tsx +20 -0
- package/src/ThemeSwitcher/index.tsx +39 -0
- package/src/TimePicker/index.tsx +142 -0
- package/src/Video/index.tsx +41 -0
- package/src/index.ts +125 -0
- package/src/message/AlertIcon.tsx +50 -0
- package/src/message/Message.tsx +108 -0
- package/src/message/index.tsx +64 -0
- package/src/message/styles.ts +25 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { css } from '@emotion/react';
|
|
2
|
+
import styled from '@emotion/styled';
|
|
3
|
+
import { m } from '@os-design/media';
|
|
4
|
+
|
|
5
|
+
import { enableScrollingStyles } from '@os-design/styles';
|
|
6
|
+
import { clr } from '@os-design/theming';
|
|
7
|
+
|
|
8
|
+
import { omitEmotionProps } from '@os-design/utils';
|
|
9
|
+
import React, { forwardRef, useRef } from 'react';
|
|
10
|
+
|
|
11
|
+
import useScrollFlags from './utils/useScrollFlags';
|
|
12
|
+
|
|
13
|
+
type JsxDivProps = Omit<JSX.IntrinsicElements['div'], 'ref'>;
|
|
14
|
+
export interface NavigationProps extends JsxDivProps {
|
|
15
|
+
/**
|
|
16
|
+
* The top component in the side navigator.
|
|
17
|
+
* E.g. the user avatar.
|
|
18
|
+
* @default undefined
|
|
19
|
+
*/
|
|
20
|
+
sideTop?: React.ReactNode;
|
|
21
|
+
/**
|
|
22
|
+
* The bottom component in the side navigator.
|
|
23
|
+
* E.g. the current tariff.
|
|
24
|
+
* @default undefined
|
|
25
|
+
*/
|
|
26
|
+
sideBottom?: React.ReactNode;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const Container = styled.div`
|
|
30
|
+
position: fixed;
|
|
31
|
+
box-sizing: border-box;
|
|
32
|
+
z-index: 101;
|
|
33
|
+
|
|
34
|
+
background-color: ${(p) => clr(p.theme.navigationColorBg)};
|
|
35
|
+
color: ${(p) => clr(p.theme.navigationColorText)};
|
|
36
|
+
|
|
37
|
+
// Tab navigator
|
|
38
|
+
bottom: 0;
|
|
39
|
+
left: 0;
|
|
40
|
+
right: 0;
|
|
41
|
+
height: calc(
|
|
42
|
+
${(p) => p.theme.navigationTabHeight}em + env(safe-area-inset-bottom)
|
|
43
|
+
);
|
|
44
|
+
border-top: 1px solid ${(p) => clr(p.theme.navigationColorBorder)};
|
|
45
|
+
|
|
46
|
+
// Side navigator
|
|
47
|
+
${m.min.md} {
|
|
48
|
+
// Reset tab navigator styles
|
|
49
|
+
right: unset;
|
|
50
|
+
height: unset;
|
|
51
|
+
border-top: unset;
|
|
52
|
+
|
|
53
|
+
left: 0;
|
|
54
|
+
top: 0;
|
|
55
|
+
bottom: 0;
|
|
56
|
+
width: ${(p) => p.theme.navigationSideWidth}em;
|
|
57
|
+
border-right: 1px solid ${(p) => clr(p.theme.navigationColorBorder)};
|
|
58
|
+
}
|
|
59
|
+
`;
|
|
60
|
+
|
|
61
|
+
const notHasSideTopStyles = (p) =>
|
|
62
|
+
!p.hasSideTop &&
|
|
63
|
+
css`
|
|
64
|
+
padding-top: ${p.theme.pageHeaderHeight[1]}em;
|
|
65
|
+
`;
|
|
66
|
+
|
|
67
|
+
const hasPrevStyles = (p) =>
|
|
68
|
+
p.hasPrev &&
|
|
69
|
+
!p.hasNext &&
|
|
70
|
+
css`
|
|
71
|
+
mask-image: linear-gradient(
|
|
72
|
+
to right,
|
|
73
|
+
transparent,
|
|
74
|
+
white ${p.theme.navigationMaskImageSize}em
|
|
75
|
+
);
|
|
76
|
+
${m.min.md} {
|
|
77
|
+
mask-image: linear-gradient(
|
|
78
|
+
to bottom,
|
|
79
|
+
transparent,
|
|
80
|
+
white ${p.theme.navigationMaskImageSize}em
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
`;
|
|
84
|
+
|
|
85
|
+
const hasNextStyles = (p) =>
|
|
86
|
+
!p.hasPrev &&
|
|
87
|
+
p.hasNext &&
|
|
88
|
+
css`
|
|
89
|
+
mask-image: linear-gradient(
|
|
90
|
+
to left,
|
|
91
|
+
transparent,
|
|
92
|
+
white ${p.theme.navigationMaskImageSize}em
|
|
93
|
+
);
|
|
94
|
+
${m.min.md} {
|
|
95
|
+
mask-image: linear-gradient(
|
|
96
|
+
to top,
|
|
97
|
+
transparent,
|
|
98
|
+
white ${p.theme.navigationMaskImageSize}em
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
`;
|
|
102
|
+
|
|
103
|
+
const hasPrevNextStyles = (p) =>
|
|
104
|
+
p.hasPrev &&
|
|
105
|
+
p.hasNext &&
|
|
106
|
+
css`
|
|
107
|
+
mask-image: linear-gradient(
|
|
108
|
+
to right,
|
|
109
|
+
transparent,
|
|
110
|
+
white ${p.theme.navigationMaskImageSize}em,
|
|
111
|
+
white calc(100% - ${p.theme.navigationMaskImageSize}em),
|
|
112
|
+
transparent
|
|
113
|
+
);
|
|
114
|
+
${m.min.md} {
|
|
115
|
+
mask-image: linear-gradient(
|
|
116
|
+
to bottom,
|
|
117
|
+
transparent,
|
|
118
|
+
white ${p.theme.navigationMaskImageSize}em,
|
|
119
|
+
white calc(100% - ${p.theme.navigationMaskImageSize}em),
|
|
120
|
+
transparent
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
`;
|
|
124
|
+
|
|
125
|
+
interface ContentProps {
|
|
126
|
+
hasSideTop: boolean;
|
|
127
|
+
hasPrev: boolean;
|
|
128
|
+
hasNext: boolean;
|
|
129
|
+
}
|
|
130
|
+
const Content = styled(
|
|
131
|
+
'div',
|
|
132
|
+
omitEmotionProps('hasSideTop', 'hasPrev', 'hasNext')
|
|
133
|
+
)<ContentProps>`
|
|
134
|
+
position: absolute;
|
|
135
|
+
top: 0;
|
|
136
|
+
right: 0;
|
|
137
|
+
bottom: 0;
|
|
138
|
+
left: 0;
|
|
139
|
+
|
|
140
|
+
display: flex;
|
|
141
|
+
flex-direction: row;
|
|
142
|
+
justify-content: space-between;
|
|
143
|
+
align-items: flex-start;
|
|
144
|
+
&::before,
|
|
145
|
+
&::after {
|
|
146
|
+
content: ''; // Insert space before the first item and after the last one
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
overflow-y: hidden;
|
|
150
|
+
${enableScrollingStyles('x', false)};
|
|
151
|
+
|
|
152
|
+
${m.min.md} {
|
|
153
|
+
// Reset tab navigator styles
|
|
154
|
+
justify-content: unset;
|
|
155
|
+
align-items: unset;
|
|
156
|
+
|
|
157
|
+
flex-direction: column;
|
|
158
|
+
|
|
159
|
+
overflow-x: hidden;
|
|
160
|
+
${enableScrollingStyles('y', false)};
|
|
161
|
+
|
|
162
|
+
${notHasSideTopStyles};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
${hasPrevStyles};
|
|
166
|
+
${hasNextStyles};
|
|
167
|
+
${hasPrevNextStyles};
|
|
168
|
+
`;
|
|
169
|
+
|
|
170
|
+
const Addon = styled.div`
|
|
171
|
+
display: none;
|
|
172
|
+
${m.min.md} {
|
|
173
|
+
display: block;
|
|
174
|
+
}
|
|
175
|
+
`;
|
|
176
|
+
|
|
177
|
+
const BottomAddon = styled(Addon)`
|
|
178
|
+
margin-top: auto;
|
|
179
|
+
`;
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* The main navigation.
|
|
183
|
+
*/
|
|
184
|
+
const Navigation = forwardRef<HTMLDivElement, NavigationProps>(
|
|
185
|
+
({ sideTop, sideBottom, children, ...rest }, ref) => {
|
|
186
|
+
const contentRef = useRef<HTMLDivElement>(null);
|
|
187
|
+
const { hasPrev, hasNext } = useScrollFlags(contentRef);
|
|
188
|
+
|
|
189
|
+
return (
|
|
190
|
+
<Container
|
|
191
|
+
role='navigation'
|
|
192
|
+
aria-label='Main navigation'
|
|
193
|
+
{...rest}
|
|
194
|
+
ref={ref}
|
|
195
|
+
>
|
|
196
|
+
<Content
|
|
197
|
+
hasSideTop={!!sideTop}
|
|
198
|
+
hasPrev={hasPrev}
|
|
199
|
+
hasNext={hasNext}
|
|
200
|
+
ref={contentRef}
|
|
201
|
+
role='list'
|
|
202
|
+
>
|
|
203
|
+
{sideTop && <Addon>{sideTop}</Addon>}
|
|
204
|
+
{children}
|
|
205
|
+
{sideBottom && <BottomAddon>{sideBottom}</BottomAddon>}
|
|
206
|
+
</Content>
|
|
207
|
+
</Container>
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
Navigation.displayName = 'Navigation';
|
|
213
|
+
|
|
214
|
+
export default Navigation;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useIsMinWidth } from '@os-design/media';
|
|
2
|
+
import { useBrowserLayoutEffect, useEvent } from '@os-design/utils';
|
|
3
|
+
import { RefObject, useCallback, useState } from 'react';
|
|
4
|
+
|
|
5
|
+
interface UseScrollFlagsRes {
|
|
6
|
+
hasPrev: boolean;
|
|
7
|
+
hasNext: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const useScrollFlags = (ref: RefObject<Element>): UseScrollFlagsRes => {
|
|
11
|
+
const [hasPrev, setHasPrev] = useState(false);
|
|
12
|
+
const [hasNext, setHasNext] = useState(false);
|
|
13
|
+
const isMinMd = useIsMinWidth('md');
|
|
14
|
+
|
|
15
|
+
const scrollHandler = useCallback(() => {
|
|
16
|
+
const { current } = ref;
|
|
17
|
+
if (!current) return;
|
|
18
|
+
|
|
19
|
+
setHasPrev(current[isMinMd ? 'scrollTop' : 'scrollLeft'] > 0);
|
|
20
|
+
setHasNext(
|
|
21
|
+
current[isMinMd ? 'scrollHeight' : 'scrollWidth'] -
|
|
22
|
+
current[isMinMd ? 'scrollTop' : 'scrollLeft'] -
|
|
23
|
+
current[isMinMd ? 'clientHeight' : 'clientWidth'] >
|
|
24
|
+
0
|
|
25
|
+
);
|
|
26
|
+
}, [ref, isMinMd]);
|
|
27
|
+
|
|
28
|
+
useBrowserLayoutEffect(() => scrollHandler(), [scrollHandler]);
|
|
29
|
+
useEvent(ref, 'scroll', scrollHandler);
|
|
30
|
+
useEvent(
|
|
31
|
+
(typeof window !== 'undefined' ? window : undefined) as EventTarget,
|
|
32
|
+
'resize',
|
|
33
|
+
scrollHandler
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
return { hasPrev, hasNext };
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export default useScrollFlags;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { css } from '@emotion/react';
|
|
2
|
+
import styled from '@emotion/styled';
|
|
3
|
+
import { m } from '@os-design/media';
|
|
4
|
+
import { resetFocusStyles, transitionStyles } from '@os-design/styles';
|
|
5
|
+
import { clr } from '@os-design/theming';
|
|
6
|
+
import { omitEmotionProps } from '@os-design/utils';
|
|
7
|
+
import React, { forwardRef } from 'react';
|
|
8
|
+
import { LinkProps, ReactRouterLinkProps } from '../Link';
|
|
9
|
+
|
|
10
|
+
type JsxAProps = Omit<JSX.IntrinsicElements['a'], 'ref'>;
|
|
11
|
+
export interface NavigationItemProps
|
|
12
|
+
extends JsxAProps,
|
|
13
|
+
ReactRouterLinkProps,
|
|
14
|
+
Pick<LinkProps, 'as'> {
|
|
15
|
+
/**
|
|
16
|
+
* The icon of the item.
|
|
17
|
+
* @default undefined
|
|
18
|
+
*/
|
|
19
|
+
icon: React.ReactElement;
|
|
20
|
+
/**
|
|
21
|
+
* Whether the item is the current page.
|
|
22
|
+
* @default false
|
|
23
|
+
*/
|
|
24
|
+
currentPage?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const currentPageStyles = (p) =>
|
|
28
|
+
p.currentPage &&
|
|
29
|
+
css`
|
|
30
|
+
background-color: ${clr(p.theme.navigationItemColorBgActive)};
|
|
31
|
+
color: ${clr(p.theme.navigationItemColorTextActive)};
|
|
32
|
+
`;
|
|
33
|
+
|
|
34
|
+
type NavigationLinkProps = Pick<NavigationItemProps, 'currentPage'>;
|
|
35
|
+
const NavigationLink = styled(
|
|
36
|
+
'a',
|
|
37
|
+
omitEmotionProps('currentPage', 'as')
|
|
38
|
+
)<NavigationLinkProps>`
|
|
39
|
+
${resetFocusStyles};
|
|
40
|
+
text-decoration: none;
|
|
41
|
+
cursor: pointer;
|
|
42
|
+
overflow: hidden;
|
|
43
|
+
border-radius: ${(p) => p.theme.borderRadius}em;
|
|
44
|
+
|
|
45
|
+
display: flex;
|
|
46
|
+
flex-direction: column;
|
|
47
|
+
justify-content: center;
|
|
48
|
+
align-items: center;
|
|
49
|
+
|
|
50
|
+
color: ${(p) => clr(p.theme.navigationItemColorText)};
|
|
51
|
+
@media (hover: hover) {
|
|
52
|
+
&:hover,
|
|
53
|
+
&:focus {
|
|
54
|
+
background-color: ${(p) => clr(p.theme.navigationItemColorBgHover)};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Tab navigator
|
|
59
|
+
width: ${(p) => p.theme.navigationItemTabWidth}em;
|
|
60
|
+
min-width: ${(p) => p.theme.navigationItemTabWidth}em;
|
|
61
|
+
height: calc(${(p) => p.theme.navigationTabHeight}em - 1px);
|
|
62
|
+
|
|
63
|
+
// Side navigator
|
|
64
|
+
${m.min.md} {
|
|
65
|
+
// Reset tab navigator styles
|
|
66
|
+
min-width: unset;
|
|
67
|
+
|
|
68
|
+
width: 100%;
|
|
69
|
+
height: ${(p) => p.theme.navigationItemSideHeight}em;
|
|
70
|
+
min-height: ${(p) => p.theme.navigationItemSideHeight}em;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
${currentPageStyles};
|
|
74
|
+
${transitionStyles('background-color', 'color')};
|
|
75
|
+
`;
|
|
76
|
+
|
|
77
|
+
const IconContainer = styled.span`
|
|
78
|
+
display: inherit;
|
|
79
|
+
|
|
80
|
+
// Tab navigator
|
|
81
|
+
font-size: 1.6em;
|
|
82
|
+
|
|
83
|
+
// Side navigator
|
|
84
|
+
${m.min.md} {
|
|
85
|
+
font-size: 1.8em;
|
|
86
|
+
}
|
|
87
|
+
`;
|
|
88
|
+
|
|
89
|
+
const Title = styled.span`
|
|
90
|
+
font-size: 0.75em;
|
|
91
|
+
font-weight: 500;
|
|
92
|
+
white-space: nowrap;
|
|
93
|
+
|
|
94
|
+
// Side navigator
|
|
95
|
+
${m.min.md} {
|
|
96
|
+
margin-top: 0.3em;
|
|
97
|
+
}
|
|
98
|
+
`;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* The item of the navigation.
|
|
102
|
+
*/
|
|
103
|
+
const NavigationItem = forwardRef<HTMLAnchorElement, NavigationItemProps>(
|
|
104
|
+
(
|
|
105
|
+
{
|
|
106
|
+
icon,
|
|
107
|
+
currentPage = false,
|
|
108
|
+
as,
|
|
109
|
+
onMouseDown = () => {},
|
|
110
|
+
children,
|
|
111
|
+
...rest
|
|
112
|
+
},
|
|
113
|
+
ref
|
|
114
|
+
) => (
|
|
115
|
+
<div role='listitem'>
|
|
116
|
+
<NavigationLink
|
|
117
|
+
currentPage={currentPage}
|
|
118
|
+
as={as}
|
|
119
|
+
onMouseDown={(e) => {
|
|
120
|
+
onMouseDown(e);
|
|
121
|
+
e.preventDefault();
|
|
122
|
+
}}
|
|
123
|
+
aria-current={currentPage ? 'page' : undefined}
|
|
124
|
+
{...rest}
|
|
125
|
+
ref={ref}
|
|
126
|
+
>
|
|
127
|
+
<IconContainer>{icon}</IconContainer>
|
|
128
|
+
{children && <Title>{children}</Title>}
|
|
129
|
+
</NavigationLink>
|
|
130
|
+
</div>
|
|
131
|
+
)
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
NavigationItem.displayName = 'NavigationItem';
|
|
135
|
+
|
|
136
|
+
export default NavigationItem;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { css } from '@emotion/react';
|
|
2
|
+
|
|
3
|
+
import styled from '@emotion/styled';
|
|
4
|
+
import { m } from '@os-design/media';
|
|
5
|
+
|
|
6
|
+
import { horizontalPaddingStyles } from '@os-design/styles';
|
|
7
|
+
import { omitEmotionProps } from '@os-design/utils';
|
|
8
|
+
import React, { forwardRef, useContext } from 'react';
|
|
9
|
+
|
|
10
|
+
import LayoutContext from '../Layout/LayoutContext';
|
|
11
|
+
|
|
12
|
+
type JsxDivProps = Omit<JSX.IntrinsicElements['div'], 'ref'>;
|
|
13
|
+
export interface PageContentProps extends JsxDivProps {
|
|
14
|
+
/**
|
|
15
|
+
* Whether there is the list in the page content.
|
|
16
|
+
* @default false
|
|
17
|
+
*/
|
|
18
|
+
hasList?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const hasNavigationStyles = (p) =>
|
|
22
|
+
p.hasNavigation &&
|
|
23
|
+
css`
|
|
24
|
+
${!p.hasList &&
|
|
25
|
+
css`
|
|
26
|
+
margin-bottom: calc(
|
|
27
|
+
${p.theme.navigationTabHeight}em + env(safe-area-inset-bottom)
|
|
28
|
+
);
|
|
29
|
+
`}
|
|
30
|
+
${m.min.md} {
|
|
31
|
+
margin-bottom: 0;
|
|
32
|
+
margin-left: ${p.theme.navigationSideWidth}em;
|
|
33
|
+
}
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
const hasPageHeaderStyles = (p) =>
|
|
37
|
+
p.hasPageHeader &&
|
|
38
|
+
!p.hasList &&
|
|
39
|
+
css`
|
|
40
|
+
margin-top: ${p.theme.pageHeaderHeight[0]}em;
|
|
41
|
+
${m.min.md} {
|
|
42
|
+
margin-top: ${p.theme.pageHeaderHeight[1]}em;
|
|
43
|
+
}
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
const notHasListStyles = (p) =>
|
|
47
|
+
!p.hasList &&
|
|
48
|
+
css`
|
|
49
|
+
padding-top: ${p.theme.pageContentPaddingVertical}em;
|
|
50
|
+
padding-bottom: ${p.theme.pageContentPaddingVertical}em;
|
|
51
|
+
${horizontalPaddingStyles()(p)};
|
|
52
|
+
`;
|
|
53
|
+
|
|
54
|
+
const notHasNavigationStyles = (p) =>
|
|
55
|
+
!p.hasNavigation &&
|
|
56
|
+
!p.hasList &&
|
|
57
|
+
css`
|
|
58
|
+
margin-bottom: env(safe-area-inset-bottom);
|
|
59
|
+
`;
|
|
60
|
+
|
|
61
|
+
interface ContainerProps {
|
|
62
|
+
hasNavigation?: boolean;
|
|
63
|
+
hasPageHeader?: boolean;
|
|
64
|
+
hasList?: boolean;
|
|
65
|
+
}
|
|
66
|
+
const Container = styled(
|
|
67
|
+
'main',
|
|
68
|
+
omitEmotionProps('hasNavigation', 'hasPageHeader', 'hasList')
|
|
69
|
+
)<ContainerProps>`
|
|
70
|
+
${hasNavigationStyles};
|
|
71
|
+
${hasPageHeaderStyles};
|
|
72
|
+
${notHasListStyles};
|
|
73
|
+
${notHasNavigationStyles};
|
|
74
|
+
`;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The wrapper that adds padding to the page content.
|
|
78
|
+
*/
|
|
79
|
+
const PageContent = forwardRef<HTMLElement, PageContentProps>(
|
|
80
|
+
({ hasList = false, children, ...rest }, ref) => {
|
|
81
|
+
const { hasNavigation, hasPageHeader } = useContext(LayoutContext);
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<Container
|
|
85
|
+
hasNavigation={hasNavigation}
|
|
86
|
+
hasPageHeader={hasPageHeader}
|
|
87
|
+
hasList={hasList}
|
|
88
|
+
{...rest}
|
|
89
|
+
ref={ref}
|
|
90
|
+
>
|
|
91
|
+
{children}
|
|
92
|
+
</Container>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
PageContent.displayName = 'PageContent';
|
|
98
|
+
|
|
99
|
+
export default PageContent;
|