datocms-react-ui 0.3.27 → 0.3.31
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/dist/cjs/FieldHint/styles.module.css.json +1 -1
- package/dist/cjs/FieldWrapper/index.js +29 -0
- package/dist/cjs/FieldWrapper/index.js.map +1 -0
- package/dist/cjs/SelectField/index.js +8 -20
- package/dist/cjs/SelectField/index.js.map +1 -1
- package/dist/cjs/TextField/index.js +2 -5
- package/dist/cjs/TextField/index.js.map +1 -1
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/FieldHint/styles.module.css.json +1 -1
- package/dist/esm/FieldWrapper/index.d.ts +13 -0
- package/dist/esm/FieldWrapper/index.js +22 -0
- package/dist/esm/FieldWrapper/index.js.map +1 -0
- package/dist/esm/SelectField/index.js +9 -21
- package/dist/esm/SelectField/index.js.map +1 -1
- package/dist/esm/TextField/index.js +3 -6
- package/dist/esm/TextField/index.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/types/FieldWrapper/index.d.ts +13 -0
- package/dist/types/index.d.ts +1 -0
- package/package.json +4 -3
- package/src/Button/index.tsx +173 -0
- package/src/Button/styles.module.css +149 -0
- package/src/Button/styles.module.css.json +1 -0
- package/src/ButtonGroup/Button/index.tsx +40 -0
- package/src/ButtonGroup/Button/styles.module.css +72 -0
- package/src/ButtonGroup/Button/styles.module.css.json +1 -0
- package/src/ButtonGroup/Group/index.tsx +31 -0
- package/src/ButtonGroup/Group/styles.module.css +6 -0
- package/src/ButtonGroup/Group/styles.module.css.json +1 -0
- package/src/ButtonGroup/index.ts +4 -0
- package/src/Canvas/index.tsx +556 -0
- package/src/Canvas/styles.module.css +75 -0
- package/src/Canvas/styles.module.css.json +1 -0
- package/src/ContextInspector/index.tsx +316 -0
- package/src/ContextInspector/styles.module.css +90 -0
- package/src/ContextInspector/styles.module.css.json +1 -0
- package/src/Dropdown/Dropdown.tsx +171 -0
- package/src/Dropdown/DropdownContext.tsx +10 -0
- package/src/Dropdown/Group.tsx +16 -0
- package/src/Dropdown/Menu.tsx +351 -0
- package/src/Dropdown/MenuContext.tsx +18 -0
- package/src/Dropdown/Option.tsx +148 -0
- package/src/Dropdown/OptionAction.tsx +42 -0
- package/src/Dropdown/Portal.tsx +46 -0
- package/src/Dropdown/Separator.tsx +13 -0
- package/src/Dropdown/Text.tsx +8 -0
- package/src/Dropdown/index.tsx +26 -0
- package/src/Dropdown/styles.module.css +331 -0
- package/src/Dropdown/styles.module.css.json +1 -0
- package/src/FieldError/index.tsx +10 -0
- package/src/FieldError/styles.module.css +6 -0
- package/src/FieldError/styles.module.css.json +1 -0
- package/src/FieldGroup/index.tsx +25 -0
- package/src/FieldGroup/styles.module.css +12 -0
- package/src/FieldGroup/styles.module.css.json +1 -0
- package/src/FieldHint/index.tsx +10 -0
- package/src/FieldHint/styles.module.css +14 -0
- package/src/FieldHint/styles.module.css.json +1 -0
- package/src/FieldWrapper/index.tsx +38 -0
- package/src/Form/index.tsx +145 -0
- package/src/Form/styles.module.css +19 -0
- package/src/Form/styles.module.css.json +1 -0
- package/src/FormLabel/index.tsx +36 -0
- package/src/FormLabel/styles.module.css +31 -0
- package/src/FormLabel/styles.module.css.json +1 -0
- package/src/Section/index.tsx +104 -0
- package/src/Section/styles.module.css +100 -0
- package/src/Section/styles.module.css.json +1 -0
- package/src/SelectField/index.tsx +242 -0
- package/src/SelectInput/index.tsx +233 -0
- package/src/SidebarPanel/index.tsx +110 -0
- package/src/SidebarPanel/styles.module.css +49 -0
- package/src/SidebarPanel/styles.module.css.json +1 -0
- package/src/Spinner/index.tsx +68 -0
- package/src/Spinner/styles.module.css +31 -0
- package/src/Spinner/styles.module.css.json +1 -0
- package/src/SwitchField/index.tsx +67 -0
- package/src/SwitchField/styles.module.css +25 -0
- package/src/SwitchField/styles.module.css.json +1 -0
- package/src/SwitchInput/index.tsx +74 -0
- package/src/SwitchInput/styles.module.css +100 -0
- package/src/SwitchInput/styles.module.css.json +1 -0
- package/src/TextField/index.tsx +51 -0
- package/src/TextField/styles.module.css +0 -0
- package/src/TextField/styles.module.css.json +1 -0
- package/src/TextInput/index.tsx +73 -0
- package/src/TextInput/styles.module.css +52 -0
- package/src/TextInput/styles.module.css.json +1 -0
- package/src/Toolbar/Button/index.tsx +32 -0
- package/src/Toolbar/Button/styles.module.css +43 -0
- package/src/Toolbar/Button/styles.module.css.json +1 -0
- package/src/Toolbar/Stack/index.tsx +33 -0
- package/src/Toolbar/Stack/styles.module.css +18 -0
- package/src/Toolbar/Stack/styles.module.css.json +1 -0
- package/src/Toolbar/Title/index.tsx +17 -0
- package/src/Toolbar/Title/styles.module.css +12 -0
- package/src/Toolbar/Title/styles.module.css.json +1 -0
- package/src/Toolbar/Toolbar/index.tsx +112 -0
- package/src/Toolbar/Toolbar/styles.module.css +15 -0
- package/src/Toolbar/Toolbar/styles.module.css.json +1 -0
- package/src/Toolbar/index.ts +8 -0
- package/src/base.css +89 -0
- package/src/generateStyleFromCtx/index.ts +25 -0
- package/src/global.css +23 -0
- package/src/icons.tsx +108 -0
- package/src/index.ts +24 -0
- package/src/mergeRefs/index.ts +8 -0
- package/src/useClickOutside/index.ts +30 -0
- package/src/useMediaQuery/index.ts +185 -0
- package/styles.css +1 -1
- package/types.json +801 -760
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import classNames from 'classnames';
|
|
2
|
+
import React, { ReactNode, useState } from 'react';
|
|
3
|
+
import s from './styles.module.css.json';
|
|
4
|
+
|
|
5
|
+
function ChevronDownIcon() {
|
|
6
|
+
return (
|
|
7
|
+
<svg
|
|
8
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
9
|
+
viewBox="0 0 448 512"
|
|
10
|
+
width="1em"
|
|
11
|
+
height="1em"
|
|
12
|
+
>
|
|
13
|
+
<path d="M207.029 381.476L12.686 187.132c-9.373-9.373-9.373-24.569 0-33.941l22.667-22.667c9.357-9.357 24.522-9.375 33.901-.04L224 284.505l154.745-154.021c9.379-9.335 24.544-9.317 33.901.04l22.667 22.667c9.373 9.373 9.373 24.569 0 33.941L240.971 381.476c-9.373 9.372-24.569 9.372-33.942 0z"></path>
|
|
14
|
+
</svg>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function ChevronUpIcon() {
|
|
19
|
+
return (
|
|
20
|
+
<svg
|
|
21
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
22
|
+
viewBox="0 0 448 512"
|
|
23
|
+
width="1em"
|
|
24
|
+
height="1em"
|
|
25
|
+
>
|
|
26
|
+
<path d="M240.971 130.524l194.343 194.343c9.373 9.373 9.373 24.569 0 33.941l-22.667 22.667c-9.357 9.357-24.522 9.375-33.901.04L224 227.495 69.255 381.516c-9.379 9.335-24.544 9.317-33.901-.04l-22.667-22.667c-9.373-9.373-9.373-24.569 0-33.941L207.03 130.525c9.372-9.373 24.568-9.373 33.941-.001z"></path>
|
|
27
|
+
</svg>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type SidebarPanelProps = {
|
|
32
|
+
title?: ReactNode;
|
|
33
|
+
startOpen?: boolean;
|
|
34
|
+
children: ReactNode;
|
|
35
|
+
noPadding?: boolean;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @example Basic example
|
|
40
|
+
*
|
|
41
|
+
* ```js
|
|
42
|
+
* <Canvas ctx={ctx}>
|
|
43
|
+
* <div style={{ display: 'flex' }}>
|
|
44
|
+
* <div
|
|
45
|
+
* style={{
|
|
46
|
+
* width: '300px',
|
|
47
|
+
* borderRight: '1px solid var(--border-color)',
|
|
48
|
+
* }}
|
|
49
|
+
* >
|
|
50
|
+
* <SidebarPanel title="Default">Content</SidebarPanel>
|
|
51
|
+
* <SidebarPanel title="Start open" startOpen>
|
|
52
|
+
* Content
|
|
53
|
+
* </SidebarPanel>
|
|
54
|
+
* <SidebarPanel title="Content with no padding" noPadding>
|
|
55
|
+
* Content
|
|
56
|
+
* </SidebarPanel>
|
|
57
|
+
* </div>
|
|
58
|
+
* <div
|
|
59
|
+
* style={{
|
|
60
|
+
* flex: '1',
|
|
61
|
+
* display: 'flex',
|
|
62
|
+
* justifyContent: 'center',
|
|
63
|
+
* alignItems: 'center',
|
|
64
|
+
* background: 'var(--light-bg-color)',
|
|
65
|
+
* }}
|
|
66
|
+
* >
|
|
67
|
+
* Main content
|
|
68
|
+
* </div>
|
|
69
|
+
* </div>
|
|
70
|
+
* </Canvas>;
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export function SidebarPanel({
|
|
74
|
+
startOpen = false,
|
|
75
|
+
title,
|
|
76
|
+
children,
|
|
77
|
+
noPadding,
|
|
78
|
+
}: SidebarPanelProps): JSX.Element {
|
|
79
|
+
const [open, setOpen] = useState(startOpen);
|
|
80
|
+
|
|
81
|
+
const handleToggle = () => {
|
|
82
|
+
setOpen(!open);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<div className={s['SidebarPanel']}>
|
|
87
|
+
{title && (
|
|
88
|
+
<button
|
|
89
|
+
type="button"
|
|
90
|
+
className={s['SidebarPanel__header']}
|
|
91
|
+
onClick={handleToggle}
|
|
92
|
+
>
|
|
93
|
+
<div className={s['SidebarPanel__header__title']}>{title}</div>
|
|
94
|
+
<div className={s['SidebarPanel__header__chevron']}>
|
|
95
|
+
{open ? <ChevronDownIcon /> : <ChevronUpIcon />}
|
|
96
|
+
</div>
|
|
97
|
+
</button>
|
|
98
|
+
)}
|
|
99
|
+
{open && (
|
|
100
|
+
<div
|
|
101
|
+
className={classNames(s['SidebarPanel__content'], {
|
|
102
|
+
[s['SidebarPanel__content--no-padding']]: noPadding,
|
|
103
|
+
})}
|
|
104
|
+
>
|
|
105
|
+
{children}
|
|
106
|
+
</div>
|
|
107
|
+
)}
|
|
108
|
+
</div>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
.SidebarPanel {
|
|
2
|
+
border-bottom: 1px solid var(--border-color);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.SidebarPanel__header {
|
|
6
|
+
font-family: inherit;
|
|
7
|
+
cursor: pointer;
|
|
8
|
+
line-height: inherit;
|
|
9
|
+
background-color: white;
|
|
10
|
+
color: var(--base-body-color);
|
|
11
|
+
-webkit-appearance: none;
|
|
12
|
+
-moz-appearance: none;
|
|
13
|
+
font-size: inherit;
|
|
14
|
+
box-sizing: border-box;
|
|
15
|
+
border: 0;
|
|
16
|
+
padding: 0;
|
|
17
|
+
text-align: left;
|
|
18
|
+
width: 100%;
|
|
19
|
+
display: flex;
|
|
20
|
+
align-items: center;
|
|
21
|
+
background-color: var(--light-bg-color);
|
|
22
|
+
user-select: none;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.SidebarPanel__header:hover,
|
|
26
|
+
.SidebarPanel__header:focus {
|
|
27
|
+
background-color: var(--lighter-bg-color);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.SidebarPanel__header__title {
|
|
31
|
+
flex: 1;
|
|
32
|
+
padding: 0 20px;
|
|
33
|
+
font-weight: 500;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.SidebarPanel__header__chevron {
|
|
37
|
+
padding: 13px 15px;
|
|
38
|
+
display: flex;
|
|
39
|
+
align-items: center;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.SidebarPanel__content {
|
|
43
|
+
padding: 20px;
|
|
44
|
+
background-color: white;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.SidebarPanel__content--no-padding {
|
|
48
|
+
padding: 0px;
|
|
49
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"SidebarPanel":"_SidebarPanel_4uwco_1","SidebarPanel__header":"_SidebarPanel__header_4uwco_5","SidebarPanel__header__title":"_SidebarPanel__header__title_4uwco_30","SidebarPanel__header__chevron":"_SidebarPanel__header__chevron_4uwco_36","SidebarPanel__content":"_SidebarPanel__content_4uwco_42","SidebarPanel__content--no-padding":"_SidebarPanel__content--no-padding_4uwco_47"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import classNames from 'classnames';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import s from './styles.module.css.json';
|
|
4
|
+
|
|
5
|
+
type SpinnerProps = {
|
|
6
|
+
size?: number;
|
|
7
|
+
placement?: 'inline' | 'centered';
|
|
8
|
+
style?: React.CSSProperties;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @example Inline spinner
|
|
13
|
+
*
|
|
14
|
+
* ```js
|
|
15
|
+
* <Canvas ctx={ctx}>
|
|
16
|
+
* Foo bar <Spinner size={24} />
|
|
17
|
+
* </Canvas>;
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* @example Centered spinner
|
|
21
|
+
*
|
|
22
|
+
* ```js
|
|
23
|
+
* <Canvas ctx={ctx}>
|
|
24
|
+
* <div style={{ height: '200px', position: 'relative' }}>
|
|
25
|
+
* <Spinner size={48} placement="centered" />
|
|
26
|
+
* </div>
|
|
27
|
+
* </Canvas>;
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export function Spinner({
|
|
31
|
+
size = 32,
|
|
32
|
+
placement = 'inline',
|
|
33
|
+
style: extraStyle = {},
|
|
34
|
+
}: SpinnerProps): JSX.Element {
|
|
35
|
+
const bars: React.ReactNode[] = [];
|
|
36
|
+
|
|
37
|
+
for (let i = 0; i < 12; i += 1) {
|
|
38
|
+
const barStyle: React.CSSProperties = {};
|
|
39
|
+
barStyle.animationDelay = `${(i - 12) / 10}s`;
|
|
40
|
+
barStyle.transform = `rotate(${i * 30}deg) translate(146%)`;
|
|
41
|
+
bars.push(<div style={barStyle} className={s['Spinner__bar']} key={i} />);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const style: React.CSSProperties = {
|
|
45
|
+
width: size * 0.5,
|
|
46
|
+
height: size * 0.5,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
if (placement === 'inline') {
|
|
50
|
+
style.marginLeft = size * 0.5;
|
|
51
|
+
style.transform = 'translateY(33%)';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<div
|
|
56
|
+
className={classNames({
|
|
57
|
+
[s['Spinner--inline']]: placement === 'inline',
|
|
58
|
+
[s['Spinner--centered']]: placement === 'centered',
|
|
59
|
+
})}
|
|
60
|
+
style={{
|
|
61
|
+
...extraStyle,
|
|
62
|
+
...style,
|
|
63
|
+
}}
|
|
64
|
+
>
|
|
65
|
+
{bars}
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
.Spinner--inline {
|
|
2
|
+
display: inline-block;
|
|
3
|
+
position: relative;
|
|
4
|
+
vertical-align: middle;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.Spinner--centered {
|
|
8
|
+
position: absolute;
|
|
9
|
+
top: 50%;
|
|
10
|
+
left: 50%;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.Spinner__bar {
|
|
14
|
+
animation: Spinner__spin 1.2s linear infinite;
|
|
15
|
+
background-color: var(--light-body-color);
|
|
16
|
+
position: absolute;
|
|
17
|
+
width: 40%;
|
|
18
|
+
height: 14%;
|
|
19
|
+
top: 0;
|
|
20
|
+
left: -20%;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@keyframes Spinner__spin {
|
|
24
|
+
0% {
|
|
25
|
+
opacity: 1;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
100% {
|
|
29
|
+
opacity: 0.15;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"Spinner--inline":"_Spinner--inline_oumod_1","Spinner--centered":"_Spinner--centered_oumod_7","Spinner__bar":"_Spinner__bar_oumod_13","Spinner__spin":"_Spinner__spin_oumod_1"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
FieldError,
|
|
4
|
+
FieldHint,
|
|
5
|
+
FormLabel,
|
|
6
|
+
FormLabelProps,
|
|
7
|
+
SwitchInput,
|
|
8
|
+
SwitchInputProps,
|
|
9
|
+
} from '..';
|
|
10
|
+
import cn from 'classnames';
|
|
11
|
+
import s from './styles.module.css.json';
|
|
12
|
+
|
|
13
|
+
type SwitchFieldProps = {
|
|
14
|
+
id: string;
|
|
15
|
+
name: string;
|
|
16
|
+
label: ReactNode;
|
|
17
|
+
hint?: ReactNode;
|
|
18
|
+
error?: ReactNode;
|
|
19
|
+
required?: boolean;
|
|
20
|
+
formLabelProps?: FormLabelProps;
|
|
21
|
+
value: SwitchInputProps['value'];
|
|
22
|
+
onChange: SwitchInputProps['onChange'];
|
|
23
|
+
switchInputProps?: SwitchInputProps;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export function SwitchField({
|
|
27
|
+
id,
|
|
28
|
+
name,
|
|
29
|
+
label,
|
|
30
|
+
hint,
|
|
31
|
+
error,
|
|
32
|
+
required,
|
|
33
|
+
formLabelProps,
|
|
34
|
+
value,
|
|
35
|
+
onChange,
|
|
36
|
+
switchInputProps,
|
|
37
|
+
}: SwitchFieldProps): JSX.Element {
|
|
38
|
+
return (
|
|
39
|
+
<>
|
|
40
|
+
<div className={s.switchField__flex}>
|
|
41
|
+
<div className={s.switchField__switchInput}>
|
|
42
|
+
<SwitchInput
|
|
43
|
+
{...switchInputProps}
|
|
44
|
+
name={name}
|
|
45
|
+
value={value}
|
|
46
|
+
onChange={onChange}
|
|
47
|
+
/>
|
|
48
|
+
</div>
|
|
49
|
+
<FormLabel
|
|
50
|
+
{...formLabelProps}
|
|
51
|
+
htmlFor={id}
|
|
52
|
+
required={required}
|
|
53
|
+
className={cn(s.switchField__label, formLabelProps?.className)}
|
|
54
|
+
error={!!error}
|
|
55
|
+
>
|
|
56
|
+
{label}
|
|
57
|
+
</FormLabel>
|
|
58
|
+
</div>
|
|
59
|
+
{(hint || error) && (
|
|
60
|
+
<div className={s.switchField__below}>
|
|
61
|
+
{error && <FieldError>{error}</FieldError>}
|
|
62
|
+
{hint && <FieldHint>{hint}</FieldHint>}
|
|
63
|
+
</div>
|
|
64
|
+
)}
|
|
65
|
+
</>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
.switchField__flex {
|
|
2
|
+
display: flex;
|
|
3
|
+
align-items: center;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.switchField__switchInput {
|
|
7
|
+
width: 55px;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.switchField__label {
|
|
11
|
+
line-height: 1.1;
|
|
12
|
+
flex: 1;
|
|
13
|
+
line-height: 20px;
|
|
14
|
+
pointer-events: none;
|
|
15
|
+
-moz-user-select: text;
|
|
16
|
+
-ms-user-select: text;
|
|
17
|
+
user-select: text;
|
|
18
|
+
margin-bottom: 0;
|
|
19
|
+
color: var(--base-body-color);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.switchField__below {
|
|
23
|
+
margin-top: var(--spacing-s);
|
|
24
|
+
margin-left: 55px;
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"switchField__flex":"_switchField__flex_16z4j_1","switchField__switchInput":"_switchField__switchInput_16z4j_6","switchField__label":"_switchField__label_16z4j_10","switchField__below":"_switchField__below_16z4j_22"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import cn from 'classnames';
|
|
3
|
+
import s from './styles.module.css.json';
|
|
4
|
+
|
|
5
|
+
export type SwitchInputChangeEventHandler = (
|
|
6
|
+
newValue: boolean,
|
|
7
|
+
event:
|
|
8
|
+
| React.MouseEvent<HTMLButtonElement>
|
|
9
|
+
| React.KeyboardEvent<HTMLButtonElement>,
|
|
10
|
+
) => void;
|
|
11
|
+
|
|
12
|
+
export interface SwitchInputProps
|
|
13
|
+
extends Omit<React.HTMLAttributes<HTMLButtonElement>, 'onChange'> {
|
|
14
|
+
name: string;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
onChange?: SwitchInputChangeEventHandler;
|
|
17
|
+
onKeyDown?: React.KeyboardEventHandler<HTMLButtonElement>;
|
|
18
|
+
value: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function SwitchInput({
|
|
22
|
+
className,
|
|
23
|
+
value,
|
|
24
|
+
disabled,
|
|
25
|
+
onClick,
|
|
26
|
+
onChange,
|
|
27
|
+
onKeyDown,
|
|
28
|
+
...restProps
|
|
29
|
+
}: SwitchInputProps): JSX.Element {
|
|
30
|
+
function triggerChange(
|
|
31
|
+
newValue: boolean,
|
|
32
|
+
event:
|
|
33
|
+
| React.MouseEvent<HTMLButtonElement>
|
|
34
|
+
| React.KeyboardEvent<HTMLButtonElement>,
|
|
35
|
+
) {
|
|
36
|
+
if (!disabled && onChange) {
|
|
37
|
+
onChange(newValue, event);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function onInternalKeyDown(e: React.KeyboardEvent<HTMLButtonElement>) {
|
|
42
|
+
if (e.key === 'ArrowLeft') {
|
|
43
|
+
triggerChange(false, e);
|
|
44
|
+
} else if (e.key === 'ArrowRight') {
|
|
45
|
+
triggerChange(true, e);
|
|
46
|
+
}
|
|
47
|
+
onKeyDown?.(e);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function onInternalClick(e: React.MouseEvent<HTMLButtonElement>) {
|
|
51
|
+
triggerChange(!value, e);
|
|
52
|
+
onClick?.(e);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const switchClassName = cn(s['switchInput'], className, {
|
|
56
|
+
[s['switchInput__checked']]: value,
|
|
57
|
+
[s['switchInput__disabled']]: disabled,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<button
|
|
62
|
+
{...restProps}
|
|
63
|
+
type="button"
|
|
64
|
+
role="switch"
|
|
65
|
+
aria-checked={value}
|
|
66
|
+
disabled={disabled}
|
|
67
|
+
className={switchClassName}
|
|
68
|
+
onKeyDown={onInternalKeyDown}
|
|
69
|
+
onClick={onInternalClick}
|
|
70
|
+
>
|
|
71
|
+
<span className={s['switchInput__inner']} />
|
|
72
|
+
</button>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
.switchInput__inner {
|
|
2
|
+
color: #fff;
|
|
3
|
+
font-size: 12px;
|
|
4
|
+
position: absolute;
|
|
5
|
+
left: 24px;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.switchInput {
|
|
9
|
+
position: relative;
|
|
10
|
+
display: inline-block;
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
width: 44px;
|
|
13
|
+
height: 22px;
|
|
14
|
+
line-height: 20px;
|
|
15
|
+
vertical-align: middle;
|
|
16
|
+
border-radius: 20px 20px;
|
|
17
|
+
border: 1px solid #ccc;
|
|
18
|
+
background-color: #ccc;
|
|
19
|
+
cursor: pointer;
|
|
20
|
+
transition: all 0.3s cubic-bezier(0.35, 0, 0.25, 1);
|
|
21
|
+
|
|
22
|
+
&:after {
|
|
23
|
+
position: absolute;
|
|
24
|
+
width: 18px;
|
|
25
|
+
height: 18px;
|
|
26
|
+
left: 2px;
|
|
27
|
+
top: 1px;
|
|
28
|
+
border-radius: 50% 50%;
|
|
29
|
+
background-color: #ffffff;
|
|
30
|
+
content: ' ';
|
|
31
|
+
cursor: pointer;
|
|
32
|
+
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.26);
|
|
33
|
+
transform: scale(1);
|
|
34
|
+
transition: left 0.3s cubic-bezier(0.35, 0, 0.25, 1);
|
|
35
|
+
animation-timing-function: cubic-bezier(0.35, 0, 0.25, 1);
|
|
36
|
+
animation-duration: 0.3s;
|
|
37
|
+
animation-name: switchInput__off;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
&:hover,
|
|
41
|
+
&:focus:after {
|
|
42
|
+
transform: scale(1.1);
|
|
43
|
+
animation-name: switchInput__on;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.switchInput__checked {
|
|
48
|
+
border: 1px solid var(--accent-color);
|
|
49
|
+
background-color: var(--accent-color);
|
|
50
|
+
|
|
51
|
+
.switchInput__inner {
|
|
52
|
+
left: 6px;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
&:after {
|
|
56
|
+
left: 22px;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.switchInput__disabled {
|
|
61
|
+
cursor: no-drop;
|
|
62
|
+
background: #ccc;
|
|
63
|
+
border-color: #ccc;
|
|
64
|
+
|
|
65
|
+
&:after {
|
|
66
|
+
background: #9e9e9e;
|
|
67
|
+
animation-name: none;
|
|
68
|
+
cursor: no-drop;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
&:hover,
|
|
72
|
+
&:focus:after {
|
|
73
|
+
transform: scale(1);
|
|
74
|
+
animation-name: none;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@keyframes switchInput__on {
|
|
79
|
+
0% {
|
|
80
|
+
transform: scale(1);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
50% {
|
|
84
|
+
transform: scale(1.25);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
100% {
|
|
88
|
+
transform: scale(1.1);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@keyframes switchInput__off {
|
|
93
|
+
0% {
|
|
94
|
+
transform: scale(1.1);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
100% {
|
|
98
|
+
transform: scale(1);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"switchInput__inner":"_switchInput__inner_1knbg_1","switchInput":"_switchInput_1knbg_1","switchInput__off":"_switchInput__off_1knbg_1","switchInput__on":"_switchInput__on_1knbg_1","switchInput__checked":"_switchInput__checked_1knbg_47","switchInput__disabled":"_switchInput__disabled_1knbg_60","none":"_none_1knbg_1"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import { FieldWrapper, FormLabelProps, TextInput, TextInputProps } from '..';
|
|
3
|
+
|
|
4
|
+
type TextFieldProps = {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
label: ReactNode;
|
|
8
|
+
hint?: ReactNode;
|
|
9
|
+
placeholder?: string;
|
|
10
|
+
error?: ReactNode;
|
|
11
|
+
required?: boolean;
|
|
12
|
+
formLabelProps?: FormLabelProps;
|
|
13
|
+
value: TextInputProps['value'];
|
|
14
|
+
onChange: TextInputProps['onChange'];
|
|
15
|
+
textInputProps?: TextInputProps;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export function TextField({
|
|
19
|
+
id,
|
|
20
|
+
name,
|
|
21
|
+
label,
|
|
22
|
+
hint,
|
|
23
|
+
error,
|
|
24
|
+
required,
|
|
25
|
+
placeholder,
|
|
26
|
+
formLabelProps,
|
|
27
|
+
value,
|
|
28
|
+
onChange,
|
|
29
|
+
textInputProps,
|
|
30
|
+
}: TextFieldProps): JSX.Element {
|
|
31
|
+
return (
|
|
32
|
+
<FieldWrapper
|
|
33
|
+
formLabelProps={formLabelProps}
|
|
34
|
+
id={id}
|
|
35
|
+
required={required}
|
|
36
|
+
error={error}
|
|
37
|
+
hint={hint}
|
|
38
|
+
label={label}
|
|
39
|
+
>
|
|
40
|
+
<TextInput
|
|
41
|
+
{...textInputProps}
|
|
42
|
+
id={id}
|
|
43
|
+
name={name}
|
|
44
|
+
value={value}
|
|
45
|
+
placeholder={placeholder}
|
|
46
|
+
onChange={onChange}
|
|
47
|
+
error={!!error}
|
|
48
|
+
/>
|
|
49
|
+
</FieldWrapper>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import React, { useCallback, RefObject, ChangeEvent } from 'react';
|
|
2
|
+
import cn from 'classnames';
|
|
3
|
+
import s from './styles.module.css.json';
|
|
4
|
+
|
|
5
|
+
export type TextInputChangeEventHandler = (
|
|
6
|
+
newValue: string,
|
|
7
|
+
event: React.ChangeEvent<HTMLInputElement>,
|
|
8
|
+
) => void;
|
|
9
|
+
|
|
10
|
+
export type TextInputProps = {
|
|
11
|
+
type?: 'text' | 'password' | 'email' | 'number' | 'search' | 'url' | 'date' | 'time';
|
|
12
|
+
name?: string;
|
|
13
|
+
labelText?: string;
|
|
14
|
+
id?: string;
|
|
15
|
+
className?: string;
|
|
16
|
+
monospaced?: boolean;
|
|
17
|
+
onChange?: TextInputChangeEventHandler;
|
|
18
|
+
inputRef?: RefObject<HTMLInputElement>;
|
|
19
|
+
error?: boolean;
|
|
20
|
+
} & Omit<JSX.IntrinsicElements['input'], 'onChange'>;
|
|
21
|
+
|
|
22
|
+
export const TextInput = ({
|
|
23
|
+
className,
|
|
24
|
+
disabled = false,
|
|
25
|
+
error,
|
|
26
|
+
id,
|
|
27
|
+
inputRef,
|
|
28
|
+
maxLength,
|
|
29
|
+
name,
|
|
30
|
+
labelText,
|
|
31
|
+
onBlur,
|
|
32
|
+
onChange,
|
|
33
|
+
placeholder,
|
|
34
|
+
required = false,
|
|
35
|
+
type,
|
|
36
|
+
value,
|
|
37
|
+
monospaced,
|
|
38
|
+
...otherProps
|
|
39
|
+
}: TextInputProps): JSX.Element => {
|
|
40
|
+
const handleChange = useCallback(
|
|
41
|
+
(e: ChangeEvent<HTMLInputElement>) => {
|
|
42
|
+
if (onChange) {
|
|
43
|
+
onChange(e.target.value, e);
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
[onChange],
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const classNames = cn(s['TextInput'], className, {
|
|
50
|
+
[s['TextInput--disabled']]: disabled,
|
|
51
|
+
[s['TextInput--error']]: error,
|
|
52
|
+
[s['TextInput--monospaced']]: monospaced,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<input
|
|
57
|
+
className={classNames}
|
|
58
|
+
aria-label={labelText}
|
|
59
|
+
id={id}
|
|
60
|
+
name={name}
|
|
61
|
+
required={required}
|
|
62
|
+
placeholder={placeholder}
|
|
63
|
+
maxLength={maxLength}
|
|
64
|
+
disabled={disabled}
|
|
65
|
+
onBlur={onBlur}
|
|
66
|
+
onChange={handleChange}
|
|
67
|
+
value={value}
|
|
68
|
+
type={type}
|
|
69
|
+
ref={inputRef}
|
|
70
|
+
{...otherProps}
|
|
71
|
+
/>
|
|
72
|
+
);
|
|
73
|
+
};
|