@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.
Files changed (99) hide show
  1. package/package.json +21 -13
  2. package/src/@types/emotion.d.ts +7 -0
  3. package/src/Alert/index.tsx +112 -0
  4. package/src/Avatar/index.tsx +173 -0
  5. package/src/Avatar/utils/nameToInitials.ts +12 -0
  6. package/src/Avatar/utils/strToHue.ts +13 -0
  7. package/src/AvatarSkeleton/index.tsx +29 -0
  8. package/src/Breadcrumb/index.tsx +93 -0
  9. package/src/BreadcrumbItem/index.tsx +83 -0
  10. package/src/Button/ButtonContent.tsx +91 -0
  11. package/src/Button/index.tsx +225 -0
  12. package/src/Button/utils/useButtonColors.ts +84 -0
  13. package/src/Checkbox/index.tsx +225 -0
  14. package/src/CheckboxSkeleton/index.tsx +50 -0
  15. package/src/DatePicker/DatePickerCalendar.tsx +220 -0
  16. package/src/DatePicker/index.tsx +568 -0
  17. package/src/Drawer/index.tsx +212 -0
  18. package/src/Form/FormConfigContext.ts +16 -0
  19. package/src/Form/index.tsx +49 -0
  20. package/src/FormDivider/index.tsx +74 -0
  21. package/src/FormItem/index.tsx +118 -0
  22. package/src/Gallery/Status.tsx +62 -0
  23. package/src/Gallery/index.tsx +290 -0
  24. package/src/GlobalStyles/index.tsx +17 -0
  25. package/src/GlobalStyles/resetStyles.ts +17 -0
  26. package/src/GlobalStyles/typographyStyles.ts +78 -0
  27. package/src/HeaderSkeleton/index.tsx +64 -0
  28. package/src/Image/index.tsx +104 -0
  29. package/src/ImageSkeleton/index.tsx +22 -0
  30. package/src/Input/index.tsx +330 -0
  31. package/src/Input/utils/getFocusableElements.ts +8 -0
  32. package/src/InputNumber/index.tsx +208 -0
  33. package/src/InputNumber/utils/defaultLocale.ts +9 -0
  34. package/src/InputPassword/index.tsx +201 -0
  35. package/src/InputPassword/utils/defaultLocale.ts +11 -0
  36. package/src/InputSearch/index.tsx +111 -0
  37. package/src/InputSearch/utils/defaultLocale.ts +9 -0
  38. package/src/InputSkeleton/index.tsx +28 -0
  39. package/src/Layout/LayoutContext.ts +21 -0
  40. package/src/Layout/index.tsx +44 -0
  41. package/src/Link/index.tsx +129 -0
  42. package/src/LinkButton/index.tsx +100 -0
  43. package/src/List/WindowScroller.tsx +53 -0
  44. package/src/List/index.tsx +255 -0
  45. package/src/List/utils/bodyPointerEvents.ts +24 -0
  46. package/src/List/utils/frameTimeout.ts +36 -0
  47. package/src/List/utils/useRWLoadNext.ts +38 -0
  48. package/src/ListItem/index.tsx +92 -0
  49. package/src/ListItemActions/index.tsx +207 -0
  50. package/src/ListItemLink/index.tsx +63 -0
  51. package/src/ListSkeleton/index.tsx +115 -0
  52. package/src/LogoLink/index.tsx +93 -0
  53. package/src/LogoLink/logo.example.svg +18 -0
  54. package/src/Menu/index.tsx +128 -0
  55. package/src/Menu/utils/useFocusWithArrows.ts +50 -0
  56. package/src/MenuDivider/index.tsx +22 -0
  57. package/src/MenuGroup/index.tsx +190 -0
  58. package/src/MenuItem/index.tsx +108 -0
  59. package/src/Modal/index.tsx +411 -0
  60. package/src/Modal/utils/defaultLocale.ts +9 -0
  61. package/src/Navigation/index.tsx +214 -0
  62. package/src/Navigation/utils/useScrollFlags.ts +39 -0
  63. package/src/NavigationItem/index.tsx +136 -0
  64. package/src/PageContent/index.tsx +99 -0
  65. package/src/PageHeader/index.tsx +246 -0
  66. package/src/PageHeader/utils/defaultLocale.ts +9 -0
  67. package/src/PageHeaderInputSearch/index.tsx +145 -0
  68. package/src/PageHeaderInputSearch/utils/defaultLocale.ts +16 -0
  69. package/src/PageHeaderSkeleton/index.tsx +33 -0
  70. package/src/ParagraphSkeleton/index.tsx +65 -0
  71. package/src/Popover/index.tsx +243 -0
  72. package/src/Popover/utils/usePopoverPosition.ts +216 -0
  73. package/src/Progress/index.tsx +100 -0
  74. package/src/RadioGroup/index.tsx +165 -0
  75. package/src/RadioGroupSkeleton/index.tsx +36 -0
  76. package/src/Result/index.tsx +109 -0
  77. package/src/ScrollButton/index.tsx +159 -0
  78. package/src/ScrollButton/utils/useContainerPosition.ts +41 -0
  79. package/src/ScrollButton/utils/useVisibility.ts +56 -0
  80. package/src/Select/index.tsx +970 -0
  81. package/src/Select/utils/defaultLocale.ts +11 -0
  82. package/src/Skeleton/index.tsx +52 -0
  83. package/src/Switch/index.tsx +217 -0
  84. package/src/SwitchSkeleton/index.tsx +30 -0
  85. package/src/Tag/index.tsx +75 -0
  86. package/src/TagLink/index.tsx +53 -0
  87. package/src/TagList/index.tsx +95 -0
  88. package/src/TagListSkeleton/index.tsx +38 -0
  89. package/src/TagSkeleton/index.tsx +40 -0
  90. package/src/TextArea/index.tsx +231 -0
  91. package/src/TextAreaSkeleton/index.tsx +20 -0
  92. package/src/ThemeSwitcher/index.tsx +39 -0
  93. package/src/TimePicker/index.tsx +142 -0
  94. package/src/Video/index.tsx +41 -0
  95. package/src/index.ts +125 -0
  96. package/src/message/AlertIcon.tsx +50 -0
  97. package/src/message/Message.tsx +108 -0
  98. package/src/message/index.tsx +64 -0
  99. package/src/message/styles.ts +25 -0
@@ -0,0 +1,225 @@
1
+ import { css } from '@emotion/react';
2
+ import styled from '@emotion/styled';
3
+ import { m } from '@os-design/media';
4
+ import {
5
+ resetButtonStyles,
6
+ sizeStyles,
7
+ transitionStyles,
8
+ WithSize,
9
+ } from '@os-design/styles';
10
+ import { clr } from '@os-design/theming';
11
+ import { omitEmotionProps } from '@os-design/utils';
12
+ import React, { forwardRef } from 'react';
13
+ import ButtonContent from './ButtonContent';
14
+ import useButtonColors, { ButtonColors } from './utils/useButtonColors';
15
+
16
+ type JsxButtonProps = Omit<
17
+ JSX.IntrinsicElements['button'],
18
+ 'type' | 'color' | 'ref'
19
+ >;
20
+
21
+ // Used by Button, LinkButton
22
+ export interface BaseButtonProps extends WithSize {
23
+ /**
24
+ * Type of button styles.
25
+ * @default primary
26
+ */
27
+ type?: 'primary' | 'outline' | 'ghost';
28
+ /**
29
+ * Sets the danger styles.
30
+ * @default false
31
+ */
32
+ danger?: boolean;
33
+ /**
34
+ * The component located on the left side.
35
+ * @default undefined
36
+ */
37
+ left?: React.ReactNode;
38
+ /**
39
+ * The component located on the right side.
40
+ * @default undefined
41
+ */
42
+ right?: React.ReactNode;
43
+ /**
44
+ * Whether the button has full width.
45
+ * Possible values:
46
+ * `default` – the button has full width if the screen width is less than xs;
47
+ * `always` – the button always has full width;
48
+ * `never` – the button never has full width.
49
+ * @default default
50
+ */
51
+ wide?: 'default' | 'always' | 'never';
52
+ /**
53
+ * Shows the loading status and disables the button.
54
+ * @default false
55
+ */
56
+ loading?: boolean;
57
+ /**
58
+ * Whether the button is disabled.
59
+ * @default false
60
+ */
61
+ disabled?: boolean;
62
+ }
63
+
64
+ export type ButtonProps = JsxButtonProps & BaseButtonProps;
65
+
66
+ interface StyledButtonProps
67
+ extends Pick<ButtonProps, 'wide' | 'loading' | 'disabled' | 'size'> {
68
+ btnType: ButtonProps['type'];
69
+ colors: ButtonColors;
70
+ }
71
+
72
+ const hoverStyles = (p) =>
73
+ !p.disabled &&
74
+ css`
75
+ @media (hover: hover) {
76
+ &:hover,
77
+ &:focus {
78
+ background-color: ${clr(p.colors.bgHover)};
79
+ }
80
+ }
81
+ `;
82
+
83
+ const primaryStyles = (p) =>
84
+ p.btnType === 'primary' &&
85
+ css`
86
+ color: ${clr(p.colors.text)};
87
+ background-color: ${clr(p.colors.bg)};
88
+ ${hoverStyles(p)};
89
+ `;
90
+
91
+ const ghostStyles = (p) =>
92
+ p.btnType === 'ghost' &&
93
+ css`
94
+ color: ${clr(p.colors.text)};
95
+ background-color: transparent;
96
+ ${hoverStyles(p)};
97
+ `;
98
+
99
+ const outlineStyles = (p) =>
100
+ p.btnType === 'outline' &&
101
+ css`
102
+ color: ${clr(p.colors.text)};
103
+ background-color: transparent;
104
+ border: 1px solid currentColor;
105
+ ${hoverStyles(p)};
106
+ `;
107
+
108
+ const wideDefaultStyles = (p) =>
109
+ p.wide === 'default' &&
110
+ css`
111
+ ${m.max.xxs} {
112
+ width: 100%;
113
+ }
114
+ `;
115
+
116
+ const wideAlwaysStyles = (p) =>
117
+ p.wide === 'always' &&
118
+ css`
119
+ width: 100%;
120
+ `;
121
+
122
+ const disabledStyles = (p) =>
123
+ p.disabled &&
124
+ css`
125
+ cursor: not-allowed;
126
+ `;
127
+
128
+ export const StyledButton = styled(
129
+ 'button',
130
+ omitEmotionProps('btnType', 'colors', 'wide', 'loading', 'size')
131
+ )<StyledButtonProps>`
132
+ ${resetButtonStyles};
133
+ position: relative; // Because LoadingContainer has an absolute position
134
+ cursor: pointer;
135
+ user-select: none;
136
+ box-sizing: border-box; // Important for LinkButton
137
+
138
+ // Disable multiline
139
+ white-space: nowrap;
140
+ overflow: hidden;
141
+
142
+ border-radius: ${(p) => p.theme.borderRadius}em;
143
+ height: ${(p) => p.theme.buttonHeight}em;
144
+ padding: 0 ${(p) => p.theme.buttonPaddingHorizontal}em;
145
+
146
+ // Do not set inline-flex, otherwise the mobile safari cuts off
147
+ // the bottom border of the button (tested in iPhone 6)
148
+ display: flex;
149
+ justify-content: center;
150
+ align-items: center;
151
+
152
+ font-weight: 500;
153
+ line-height: 1;
154
+
155
+ ${primaryStyles};
156
+ ${outlineStyles};
157
+ ${ghostStyles};
158
+
159
+ ${wideDefaultStyles};
160
+ ${wideAlwaysStyles};
161
+
162
+ ${disabledStyles};
163
+
164
+ ${sizeStyles};
165
+ ${transitionStyles('background-color', 'color')};
166
+ `;
167
+
168
+ /**
169
+ * Used to trigger the corresponding business logic.
170
+ */
171
+ const Button = forwardRef<HTMLButtonElement, ButtonProps>(
172
+ (
173
+ {
174
+ type = 'primary',
175
+ danger = false,
176
+ left,
177
+ right,
178
+ wide = 'default',
179
+ loading = false,
180
+ disabled = false,
181
+ size,
182
+ children,
183
+ onMouseDown = () => {},
184
+ ...rest
185
+ },
186
+ ref
187
+ ) => {
188
+ const { buttonColors, loadingColors } = useButtonColors({
189
+ type,
190
+ danger,
191
+ disabled,
192
+ });
193
+
194
+ return (
195
+ <StyledButton
196
+ btnType={type}
197
+ colors={buttonColors}
198
+ wide={wide}
199
+ loading={loading}
200
+ disabled={disabled || loading}
201
+ size={size}
202
+ onMouseDown={(e) => {
203
+ onMouseDown(e);
204
+ e.preventDefault();
205
+ }}
206
+ aria-busy={loading}
207
+ {...rest}
208
+ ref={ref}
209
+ >
210
+ <ButtonContent
211
+ left={left}
212
+ right={right}
213
+ loading={loading}
214
+ loadingColors={loadingColors}
215
+ >
216
+ {children}
217
+ </ButtonContent>
218
+ </StyledButton>
219
+ );
220
+ }
221
+ );
222
+
223
+ Button.displayName = 'Button';
224
+
225
+ export default Button;
@@ -0,0 +1,84 @@
1
+ import { Color, useTheme } from '@os-design/theming';
2
+ import { useMemo } from 'react';
3
+
4
+ interface Props {
5
+ type: 'primary' | 'outline' | 'ghost';
6
+ danger: boolean;
7
+ disabled: boolean;
8
+ }
9
+
10
+ export interface ButtonColors {
11
+ text: Color;
12
+ bg?: Color;
13
+ bgHover?: Color;
14
+ }
15
+
16
+ export interface LoadingColors {
17
+ text: Color;
18
+ bg: Color;
19
+ }
20
+
21
+ interface UseButtonColorsRes {
22
+ buttonColors: ButtonColors;
23
+ loadingColors: LoadingColors;
24
+ }
25
+
26
+ // Used by Button, LinkButton
27
+ const useButtonColors = ({
28
+ type,
29
+ danger,
30
+ disabled,
31
+ }: Props): UseButtonColorsRes => {
32
+ const { theme } = useTheme();
33
+
34
+ const prefix = useMemo<string>(() => {
35
+ if (danger) return 'Danger';
36
+ return '';
37
+ }, [danger]);
38
+
39
+ const buttonColors = useMemo<ButtonColors>(() => {
40
+ if (type === 'primary') {
41
+ return !disabled
42
+ ? {
43
+ text: theme[`button${prefix}PrimaryColorText`],
44
+ bg: theme[`button${prefix}PrimaryColorBg`],
45
+ bgHover: theme[`button${prefix}PrimaryColorBgHover`],
46
+ }
47
+ : {
48
+ text: theme.buttonDisabledPrimaryColorText,
49
+ bg: theme.buttonDisabledPrimaryColorBg,
50
+ };
51
+ }
52
+ return !disabled
53
+ ? {
54
+ text: theme[`button${prefix}GhostColorText`],
55
+ bgHover: theme[`button${prefix}GhostColorBgHover`],
56
+ }
57
+ : {
58
+ text: theme.buttonDisabledGhostColorText,
59
+ };
60
+ }, [type, disabled, theme, prefix]);
61
+
62
+ const loadingColors = useMemo<LoadingColors>(() => {
63
+ if (disabled) {
64
+ return {
65
+ text: theme.buttonDisabledPrimaryColorText,
66
+ bg: theme.buttonDisabledPrimaryColorBg,
67
+ };
68
+ }
69
+ if (type === 'primary') {
70
+ return {
71
+ text: theme[`button${prefix}PrimaryColorLoadingText`],
72
+ bg: theme[`button${prefix}PrimaryColorLoadingBg`],
73
+ };
74
+ }
75
+ return {
76
+ text: theme[`button${prefix}GhostColorLoadingText`],
77
+ bg: theme[`button${prefix}GhostColorLoadingBg`],
78
+ };
79
+ }, [disabled, type, theme, prefix]);
80
+
81
+ return { buttonColors, loadingColors };
82
+ };
83
+
84
+ export default useButtonColors;
@@ -0,0 +1,225 @@
1
+ import { css } from '@emotion/react';
2
+ import styled from '@emotion/styled';
3
+ import { Check } from '@os-design/icons';
4
+ import {
5
+ resetFocusStyles,
6
+ sizeStyles,
7
+ transitionStyles,
8
+ WithSize,
9
+ } from '@os-design/styles';
10
+ import { clr } from '@os-design/theming';
11
+ import { omitEmotionProps, useForwardedState } from '@os-design/utils';
12
+ import React, { forwardRef } from 'react';
13
+
14
+ type JsxLabelProps = Omit<
15
+ JSX.IntrinsicElements['label'],
16
+ 'defaultValue' | 'onChange' | 'onClick' | 'ref'
17
+ >;
18
+ export interface CheckboxProps extends JsxLabelProps, WithSize {
19
+ /**
20
+ * Whether the checkbox is disabled.
21
+ * @default false
22
+ */
23
+ disabled?: boolean;
24
+ /**
25
+ * Whether the checkbox is checked.
26
+ * @default false
27
+ */
28
+ value?: boolean;
29
+ /**
30
+ * The default value.
31
+ * @default undefined
32
+ */
33
+ defaultValue?: boolean;
34
+ /**
35
+ * The change event handler.
36
+ * @default undefined
37
+ */
38
+ onChange?: (value: boolean) => void;
39
+ }
40
+
41
+ const uncheckedIconStyles = (p) =>
42
+ !p.checked &&
43
+ css`
44
+ background-color: ${clr(p.theme.checkboxUncheckedColorBg)};
45
+ border-color: ${clr(p.theme.checkboxUncheckedColorBorder)};
46
+ `;
47
+
48
+ const checkedIconStyles = (p) =>
49
+ p.checked &&
50
+ css`
51
+ background-color: ${clr(p.theme.checkboxCheckedColorBg)};
52
+ border-color: ${clr(p.theme.checkboxCheckedColorBg)};
53
+ `;
54
+
55
+ const disabledIconStyles = (p) =>
56
+ p.disabled &&
57
+ css`
58
+ background-color: ${clr(p.theme.checkboxDisabledColorBg)};
59
+ color: ${clr(p.theme.checkboxDisabledColorIcon)};
60
+ border-color: ${clr(p.theme.checkboxDisabledColorBorder)};
61
+ `;
62
+
63
+ interface IconContainerProps extends Required<Pick<CheckboxProps, 'disabled'>> {
64
+ checked: Required<CheckboxProps['value']>;
65
+ }
66
+ const IconContainer = styled(
67
+ 'span',
68
+ omitEmotionProps('disabled', 'checked')
69
+ )<IconContainerProps>`
70
+ width: ${(p) => p.theme.checkboxSize}em;
71
+ height: ${(p) => p.theme.checkboxSize}em;
72
+ min-width: ${(p) => p.theme.checkboxSize}em;
73
+ min-height: ${(p) => p.theme.checkboxSize}em;
74
+
75
+ display: flex;
76
+ justify-content: center;
77
+ align-items: center;
78
+
79
+ box-sizing: border-box;
80
+ line-height: 1;
81
+
82
+ border: 1px solid transparent;
83
+ border-radius: ${(p) => p.theme.borderRadius}em;
84
+ color: ${(p) => clr(p.theme.checkboxCheckedColorIcon)};
85
+ margin-top: ${(p) => (p.theme.lineHeight - p.theme.checkboxSize) / 2}em;
86
+
87
+ ${uncheckedIconStyles};
88
+ ${checkedIconStyles};
89
+ ${disabledIconStyles};
90
+ ${transitionStyles('background-color', 'color', 'border-color')};
91
+ `;
92
+
93
+ const hoverUncheckedIconStyles = (p) =>
94
+ !p.checked &&
95
+ css`
96
+ background-color: ${clr(p.theme.checkboxUncheckedColorBgHover)};
97
+ `;
98
+
99
+ const hoverCheckedIconStyles = (p) =>
100
+ p.checked &&
101
+ css`
102
+ background-color: ${clr(p.theme.checkboxCheckedColorBgHover)};
103
+ border-color: ${clr(p.theme.checkboxCheckedColorBgHover)};
104
+ `;
105
+
106
+ const hoverStyles = (p) =>
107
+ !p.disabled &&
108
+ css`
109
+ @media (hover: hover) {
110
+ &:hover,
111
+ &:focus {
112
+ & > span {
113
+ ${hoverUncheckedIconStyles(p)};
114
+ ${hoverCheckedIconStyles(p)};
115
+ }
116
+ }
117
+ }
118
+ `;
119
+
120
+ const disabledContainerStyles = (p) =>
121
+ p.disabled &&
122
+ css`
123
+ cursor: not-allowed;
124
+ `;
125
+
126
+ interface ContainerProps
127
+ extends Required<Pick<CheckboxProps, 'disabled'>>,
128
+ Pick<CheckboxProps, 'size'> {
129
+ checked: Required<CheckboxProps['value']>;
130
+ }
131
+ const Container = styled(
132
+ 'label',
133
+ omitEmotionProps('disabled', 'size', 'checked')
134
+ )<ContainerProps>`
135
+ ${resetFocusStyles};
136
+ cursor: pointer;
137
+ user-select: none;
138
+
139
+ display: flex;
140
+ margin: ${(p) => p.theme.checkboxVerticalIndent}em 0;
141
+
142
+ ${hoverStyles};
143
+ ${disabledContainerStyles};
144
+ ${sizeStyles};
145
+ ${transitionStyles('color')};
146
+ `;
147
+
148
+ const disabledTextStyles = (p) =>
149
+ p.disabled &&
150
+ css`
151
+ color: ${clr(p.theme.checkboxDisabledColorText)};
152
+ `;
153
+
154
+ type TextProps = Required<Pick<CheckboxProps, 'disabled'>>;
155
+ const Text = styled('div', omitEmotionProps('disabled'))<TextProps>`
156
+ margin-left: 0.4em;
157
+ color: ${(p) => clr(p.theme.colorText)};
158
+ ${disabledTextStyles};
159
+ `;
160
+
161
+ /**
162
+ * The checkbox that can be enabled or disabled.
163
+ */
164
+ const Checkbox = forwardRef<HTMLLabelElement, CheckboxProps>(
165
+ (
166
+ {
167
+ disabled = false,
168
+ value,
169
+ defaultValue,
170
+ onChange = () => {},
171
+ size,
172
+ onKeyDown = () => {},
173
+ onMouseDown = () => {},
174
+ children,
175
+ ...rest
176
+ },
177
+ ref
178
+ ) => {
179
+ const [forwardedValue, setForwardedValue] = useForwardedState({
180
+ value,
181
+ defaultValue,
182
+ onChange,
183
+ });
184
+
185
+ return (
186
+ <Container
187
+ disabled={disabled}
188
+ checked={forwardedValue}
189
+ size={size}
190
+ tabIndex={!disabled ? 0 : -1}
191
+ onClick={() => {
192
+ if (disabled) return;
193
+ setForwardedValue(!forwardedValue);
194
+ }}
195
+ onKeyDown={(e) => {
196
+ if (disabled) return;
197
+ if (['Enter', ' '].includes(e.key)) {
198
+ setForwardedValue(!forwardedValue);
199
+ e.preventDefault();
200
+ }
201
+ onKeyDown(e);
202
+ }}
203
+ onMouseDown={(e) => {
204
+ onMouseDown(e);
205
+ e.preventDefault();
206
+ }}
207
+ role='checkbox'
208
+ aria-checked={forwardedValue}
209
+ aria-disabled={disabled}
210
+ {...rest}
211
+ ref={ref}
212
+ >
213
+ <IconContainer disabled={disabled} checked={forwardedValue}>
214
+ {forwardedValue && <Check />}
215
+ </IconContainer>
216
+
217
+ {children && <Text disabled={disabled}>{children}</Text>}
218
+ </Container>
219
+ );
220
+ }
221
+ );
222
+
223
+ Checkbox.displayName = 'Checkbox';
224
+
225
+ export default Checkbox;
@@ -0,0 +1,50 @@
1
+ import styled from '@emotion/styled';
2
+
3
+ import { sizeStyles, WithSize } from '@os-design/styles';
4
+ import { clr } from '@os-design/theming';
5
+
6
+ import { omitEmotionProps } from '@os-design/utils';
7
+ import React, { forwardRef } from 'react';
8
+
9
+ import Skeleton from '../Skeleton';
10
+
11
+ type JsxDivProps = Omit<JSX.IntrinsicElements['div'], 'ref'>;
12
+ export type CheckboxSkeletonProps = JsxDivProps & WithSize;
13
+
14
+ const Container = styled('div', omitEmotionProps('size'))<WithSize>`
15
+ user-select: none;
16
+ display: flex;
17
+ margin: ${(p) => p.theme.checkboxVerticalIndent}em 0;
18
+ ${sizeStyles};
19
+ `;
20
+
21
+ const ImageSkeleton = styled(Skeleton)`
22
+ width: ${(p) => p.theme.checkboxSize}em;
23
+ height: ${(p) => p.theme.checkboxSize}em;
24
+ min-width: ${(p) => p.theme.checkboxSize}em;
25
+ min-height: ${(p) => p.theme.checkboxSize}em;
26
+
27
+ border-radius: ${(p) => p.theme.borderRadius}em;
28
+ margin-top: ${(p) => (p.theme.lineHeight - p.theme.checkboxSize) / 2}em;
29
+ `;
30
+
31
+ const Text = styled.div`
32
+ margin-left: 0.4em;
33
+ color: ${(p) => clr(p.theme.colorText)};
34
+ `;
35
+
36
+ /**
37
+ * Provides a checkbox placeholder while a user waits for the content to load.
38
+ */
39
+ const CheckboxSkeleton = forwardRef<HTMLDivElement, CheckboxSkeletonProps>(
40
+ ({ children, ...rest }, ref) => (
41
+ <Container role='checkbox' aria-busy {...rest} ref={ref}>
42
+ <ImageSkeleton />
43
+ {children && <Text>{children}</Text>}
44
+ </Container>
45
+ )
46
+ );
47
+
48
+ CheckboxSkeleton.displayName = 'CheckboxSkeleton';
49
+
50
+ export default CheckboxSkeleton;