datocms-react-ui 0.3.26 → 0.3.30

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 (92) hide show
  1. package/dist/cjs/FieldHint/styles.module.css.json +1 -1
  2. package/dist/esm/FieldHint/styles.module.css.json +1 -1
  3. package/package.json +4 -3
  4. package/src/Button/index.tsx +173 -0
  5. package/src/Button/styles.module.css +149 -0
  6. package/src/Button/styles.module.css.json +1 -0
  7. package/src/ButtonGroup/Button/index.tsx +40 -0
  8. package/src/ButtonGroup/Button/styles.module.css +72 -0
  9. package/src/ButtonGroup/Button/styles.module.css.json +1 -0
  10. package/src/ButtonGroup/Group/index.tsx +31 -0
  11. package/src/ButtonGroup/Group/styles.module.css +6 -0
  12. package/src/ButtonGroup/Group/styles.module.css.json +1 -0
  13. package/src/ButtonGroup/index.ts +4 -0
  14. package/src/Canvas/index.tsx +556 -0
  15. package/src/Canvas/styles.module.css +75 -0
  16. package/src/Canvas/styles.module.css.json +1 -0
  17. package/src/ContextInspector/index.tsx +316 -0
  18. package/src/ContextInspector/styles.module.css +90 -0
  19. package/src/ContextInspector/styles.module.css.json +1 -0
  20. package/src/Dropdown/Dropdown.tsx +171 -0
  21. package/src/Dropdown/DropdownContext.tsx +10 -0
  22. package/src/Dropdown/Group.tsx +16 -0
  23. package/src/Dropdown/Menu.tsx +351 -0
  24. package/src/Dropdown/MenuContext.tsx +18 -0
  25. package/src/Dropdown/Option.tsx +148 -0
  26. package/src/Dropdown/OptionAction.tsx +42 -0
  27. package/src/Dropdown/Portal.tsx +46 -0
  28. package/src/Dropdown/Separator.tsx +13 -0
  29. package/src/Dropdown/Text.tsx +8 -0
  30. package/src/Dropdown/index.tsx +26 -0
  31. package/src/Dropdown/styles.module.css +331 -0
  32. package/src/Dropdown/styles.module.css.json +1 -0
  33. package/src/FieldError/index.tsx +10 -0
  34. package/src/FieldError/styles.module.css +6 -0
  35. package/src/FieldError/styles.module.css.json +1 -0
  36. package/src/FieldGroup/index.tsx +25 -0
  37. package/src/FieldGroup/styles.module.css +12 -0
  38. package/src/FieldGroup/styles.module.css.json +1 -0
  39. package/src/FieldHint/index.tsx +10 -0
  40. package/src/FieldHint/styles.module.css +14 -0
  41. package/src/FieldHint/styles.module.css.json +1 -0
  42. package/src/Form/index.tsx +145 -0
  43. package/src/Form/styles.module.css +19 -0
  44. package/src/Form/styles.module.css.json +1 -0
  45. package/src/FormLabel/index.tsx +36 -0
  46. package/src/FormLabel/styles.module.css +31 -0
  47. package/src/FormLabel/styles.module.css.json +1 -0
  48. package/src/Section/index.tsx +104 -0
  49. package/src/Section/styles.module.css +100 -0
  50. package/src/Section/styles.module.css.json +1 -0
  51. package/src/SelectField/index.tsx +244 -0
  52. package/src/SelectInput/index.tsx +233 -0
  53. package/src/SidebarPanel/index.tsx +110 -0
  54. package/src/SidebarPanel/styles.module.css +49 -0
  55. package/src/SidebarPanel/styles.module.css.json +1 -0
  56. package/src/Spinner/index.tsx +68 -0
  57. package/src/Spinner/styles.module.css +31 -0
  58. package/src/Spinner/styles.module.css.json +1 -0
  59. package/src/SwitchField/index.tsx +67 -0
  60. package/src/SwitchField/styles.module.css +25 -0
  61. package/src/SwitchField/styles.module.css.json +1 -0
  62. package/src/SwitchInput/index.tsx +74 -0
  63. package/src/SwitchInput/styles.module.css +100 -0
  64. package/src/SwitchInput/styles.module.css.json +1 -0
  65. package/src/TextField/index.tsx +58 -0
  66. package/src/TextField/styles.module.css +0 -0
  67. package/src/TextField/styles.module.css.json +1 -0
  68. package/src/TextInput/index.tsx +73 -0
  69. package/src/TextInput/styles.module.css +52 -0
  70. package/src/TextInput/styles.module.css.json +1 -0
  71. package/src/Toolbar/Button/index.tsx +32 -0
  72. package/src/Toolbar/Button/styles.module.css +43 -0
  73. package/src/Toolbar/Button/styles.module.css.json +1 -0
  74. package/src/Toolbar/Stack/index.tsx +33 -0
  75. package/src/Toolbar/Stack/styles.module.css +18 -0
  76. package/src/Toolbar/Stack/styles.module.css.json +1 -0
  77. package/src/Toolbar/Title/index.tsx +17 -0
  78. package/src/Toolbar/Title/styles.module.css +12 -0
  79. package/src/Toolbar/Title/styles.module.css.json +1 -0
  80. package/src/Toolbar/Toolbar/index.tsx +112 -0
  81. package/src/Toolbar/Toolbar/styles.module.css +15 -0
  82. package/src/Toolbar/Toolbar/styles.module.css.json +1 -0
  83. package/src/Toolbar/index.ts +8 -0
  84. package/src/base.css +89 -0
  85. package/src/generateStyleFromCtx/index.ts +25 -0
  86. package/src/global.css +23 -0
  87. package/src/icons.tsx +108 -0
  88. package/src/index.ts +23 -0
  89. package/src/mergeRefs/index.ts +8 -0
  90. package/src/useClickOutside/index.ts +30 -0
  91. package/src/useMediaQuery/index.ts +185 -0
  92. package/styles.css +1 -1
@@ -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,58 @@
1
+ import React, { ReactNode } from 'react';
2
+ import {
3
+ FieldError,
4
+ FieldHint,
5
+ FormLabel,
6
+ FormLabelProps,
7
+ TextInput,
8
+ TextInputProps,
9
+ } from '..';
10
+
11
+ type TextFieldProps = {
12
+ id: string;
13
+ name: string;
14
+ label: ReactNode;
15
+ hint?: ReactNode;
16
+ placeholder?: string;
17
+ error?: ReactNode;
18
+ required?: boolean;
19
+ formLabelProps?: FormLabelProps;
20
+ value: TextInputProps['value'];
21
+ onChange: TextInputProps['onChange'];
22
+ textInputProps?: TextInputProps;
23
+ };
24
+
25
+ export function TextField({
26
+ id,
27
+ name,
28
+ label,
29
+ hint,
30
+ error,
31
+ required,
32
+ placeholder,
33
+ formLabelProps,
34
+ value,
35
+ onChange,
36
+ textInputProps,
37
+ }: TextFieldProps): JSX.Element {
38
+ return (
39
+ <>
40
+ <FormLabel {...formLabelProps} htmlFor={id} required={required} error={!!error}>
41
+ {label}
42
+ </FormLabel>
43
+
44
+ <TextInput
45
+ {...textInputProps}
46
+ id={id}
47
+ name={name}
48
+ value={value}
49
+ placeholder={placeholder}
50
+ onChange={onChange}
51
+ error={!!error}
52
+ />
53
+
54
+ {error && <FieldError>{error}</FieldError>}
55
+ {hint && <FieldHint>{hint}</FieldHint>}
56
+ </>
57
+ );
58
+ }
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
+ };